Python 3.6 : Either I miss something either generic typing breaks super chaining for inheritance
Clash Royale CLAN TAG#URR8PPP
Python 3.6 : Either I miss something either generic typing breaks super chaining for inheritance
First I ran the following code, which went real fine :
class Monster:
def __init__(self):
self._can_do =
print("created a monster")
super().__init__()
class Race(Monster):
""" all races must derive from this """
def __init__(self):
super().__init__()
print("created a race x")
class Human(Race):
def __init__(self):
super().__init__()
self._can_do.append("Do nothing special !")
print("created a human")
class Elf(Race):
def __init__(self):
super().__init__()
self._can_do.append("Avoid sleep")
print("created an elf")
class Class:
""" all classes must derive from this """
def __init__(self):
super().__init__()
print("created a class x")
class Fighter(Class):
def __init__(self):
super().__init__()
self._can_do.append("Hit hard")
print("created a fighter")
class Wizard(Class):
def __init__(self):
super().__init__()
self._can_do.append("Cast spells")
print("created a wizard")
class Hero(Human, Fighter):
def __init__(self):
x = super()
print(f"super = x")
super().__init__()
def speak(self):
for action in self._can_do:
print(f"I can action !")
print("creating hero 1 :")
hero1 = Hero()
print("hero 1 human fighter says :")
hero1.speak()
Result was :
creating hero 1 :
created a monster
created a class x
created a fighter
created a race x
created a human
hero 1 human fighter says :
I can Hit hard !
I can Do nothing special ! !
Then I had another go, changing very slightly the code, as below, because that is were I want to go :
(made the Hero class heriting dynamically instead of statically)
import typing
class Monster:
def __init__(self):
self._can_do =
print("created a monster")
super().__init__()
class Race(Monster):
""" all races must derive from this """
def __init__(self):
super().__init__()
print("created a race x")
class Human(Race):
def __init__(self):
super().__init__()
self._can_do.append("Do nothing special !")
print("created a human")
class Elf(Race):
def __init__(self):
super().__init__()
self._can_do.append("Avoid sleep")
print("created an elf")
class Class:
""" all classes must derive from this """
def __init__(self):
super().__init__()
print("created a class x")
class Fighter(Class):
def __init__(self):
super().__init__()
self._can_do.append("Hit hard")
print("created a fighter")
class Wizard(Class):
def __init__(self):
super().__init__()
self._can_do.append("Cast spells")
print("created a wizard")
RaceT = typing.TypeVar('RaceT', bound=Race)
ClassT = typing.TypeVar('ClassT', bound=Class)
class Hero(typing.Generic[RaceT, ClassT]):
def __init__(self):
super().__init__()
def speak(self):
for action in self._can_do:
print(f"I can action !")
print("creating hero 1 :")
hero1 = Hero[Human,Fighter]()
print("hero 1 human fighter says :")
hero1.speak()
This time, all went wrong :
creating hero 1 :
hero 1 human fighter says :
Traceback (most recent call last):
File "./test2.py", line 61, in <module>
hero1.speak()
File "./test2.py", line 54, in speak
for action in self._can_do:
AttributeError: 'Hero' object has no attribute '_can_do'
Seems using generic class creation makes super unable to find parent class initializer, non ?
Did I miss something ?
typing.Generic
List[int]
Tuple[str, float]
Your title is bordering on nonsensical
– Mad Physicist
Aug 10 at 16:16
2 Answers
2
That's not what generic typing means. When you declare
class Hero(typing.Generic[RaceT, ClassT]):
...
then that means that Hero
takes two type parameters. It doesn't mean that Hero[Human, Fighter]
is a subclass of Human
or Fighter
, any more than List[int]
is a subclass of int
. Generic typing is not a way to dynamically adjust a class's superclasses.
Hero
Hero[Human, Fighter]
Human
Fighter
List[int]
int
Here's a metaclass solution that allows you to dynamically generate classes from a Race, Class
pair
Race, Class
class Hero(type):
instances = # All Elf Wizards should have the same class, for example
@classmethod
def class_factory(metacls, race, class_):
if not (issubclass(race, Race) and issubclass(class_, Class)):
raise ValueError("Needs race and class, got and ".format(race, class_))
name = "0.__name__1.__name__".format(race, class_)
if name in metacls.instances:
return metacls.instances[name]
cls = metacls(name, (race, class_), )
metacls.instances[name] = cls
return cls
ElfWizard = Hero.class_factory(Elf, Wizard)
assert ElfWizard is Hero.class_factory(Elf, Wizard)
tim = ElfWizard()
prints
created a monster
created a class x
created a wizard
created a race x
created an elf
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Yes you missed something. That is not what
typing.Generic
is for at all. A generic type is normally used to describe what types a container holds such asList[int]
orTuple[str, float]
– FHTMitchell
Aug 10 at 16:13