Python 3.6 : Either I miss something either generic typing breaks super chaining for inheritance

The name of the pictureThe name of the pictureThe name of the pictureClash 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 ?





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 as List[int] or Tuple[str, float]
– FHTMitchell
Aug 10 at 16:13



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.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard