Python 3.6: O se me escapa algo, ya sea la escritura genérica rompe el superencadenamiento por herencia

Primero ejecuté el siguiente código, que salió muy bien:

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() 

El resultado fue:

 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 ! ! 

Luego tuve otra oportunidad, cambiando ligeramente el código, como se muestra a continuación, porque es donde quiero ir:

(hizo la clase de héroe heredando dinámicamente en lugar de estáticamente)

 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() 

Esta vez, todo salió mal:

 creating hero 1 : hero 1 human fighter says : Traceback (most recent call last): File "./test2.py", line 61, in  hero1.speak() File "./test2.py", line 54, in speak for action in self._can_do: AttributeError: 'Hero' object has no attribute '_can_do' 
  • Parece que la creación de una clase genérica hace que el super no pueda encontrar el inicializador de la clase padre, ¿no?

  • Me he perdido algo ?

Eso no es lo que significa la tipificación genérica. Cuando usted declara

 class Hero(typing.Generic[RaceT, ClassT]): ... 

entonces eso significa que Hero toma dos parámetros de tipo. No significa que Hero[Human, Fighter] sea ​​una subclase de Human o Fighter , más que List[int] es una subclase de int . La escritura genérica no es una manera de ajustar dinámicamente las superclases de una clase.

Aquí hay una solución de metaclase que le permite generar dinámicamente clases a partir de una Race, Class par de 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() 

huellas dactilares

 created a monster created a class x created a wizard created a race x created an elf