what método meaning python inheritance multiple-inheritance mixins

método - python class__ init



¿Las funciones__init__ de la clase Mixin no se llaman automáticamente? (3)

Me gustaría usar un Mixin para agregar siempre algunas funcionalidades init a mis clases secundarias, que cada una hereda de diferentes clases base de API. Específicamente, me gustaría crear múltiples clases secundarias diferentes que hereden de una de estas diferentes clases base provistas por API y la única Mixin, que siempre tendrá el código de inicialización de Mixin ejecutado de la misma manera, sin replicación de código. Sin embargo, parece que la función __init__ de la clase Mixin nunca se llama a menos que lo llame explícitamente en la función __init__ de la clase Child, que es menos que ideal. He creado un caso de prueba simple:

class APIBaseClassOne(object): def __init__(self, *args, **kwargs): print (" base ") class SomeMixin(object): def __init__(self, *args, **kwargs): print (" mixin before ") super(SomeMixin, self).__init__(*args, **kwargs) print (" mixin after ") class MyClass(APIBaseClassOne): pass class MixedClass(MyClass, SomeMixin): pass

Como puede ver en la siguiente salida, nunca se llama a la función de la función Mixin:

>>> import test >>> test.MixedClass() base <test.MixedClass object at 0x1004cc850>

¿Hay alguna manera de hacer esto (hacer que se llame a una función init en un Mixin) sin escribir cada clase secundaria para invocar explícitamente la función init de Mixin? (es decir, sin tener que hacer algo como esto en todas las clases :)

class MixedClass(MyClass, SomeMixin): def __init__(*args, **kwargs): SomeMixin.__init__(self, *args, **kwargs) MyClass.__init__(self, *args, **kwargs)

Por cierto, si todas mis clases secundarias heredaran de la misma clase base, me doy cuenta de que podría crear una nueva clase media que herede de la clase base y la mixin y mantenerla SECA de esa manera. Sin embargo, heredan de diferentes clases base con funcionalidad común. (Clases de campo de Django, para ser precisos).


Haga que la clase base invoque a super().__init__() aunque sea una subclase de object . De esa forma se __init__() todos los __init__() .

class BaseClassOne(object): def __init__(self, *args, **kwargs): super(BaseClassOne, self).__init__(*args, **kwargs) print (" base ")


Lamento haber visto esto tan tarde, pero

class MixedClass2(SomeMixin, MyClass): pass >>> m = MixedClass2() mixin before base mixin after

El patrón del que habla Ignacio se llama herencia múltiple cooperativa, y es genial. Pero si una clase base no está interesada en cooperar, conviértalo en la segunda base, y tu mezcla en la primera. La __init__() mixin (y cualquier otra cosa que defina) se verificará antes de la clase base, siguiendo el MRO de Python.

Esto debería resolver la pregunta general, aunque no estoy seguro de que maneje su uso específico. Las clases base con metaclases personalizadas (como los modelos de Django) o con decoradores extraños (como la respuesta de @martineau;) pueden hacer cosas locas.


Python no realiza llamadas implícitas a los métodos __init__ de la (s) súper clase (s) de una clase, pero es posible hacerlo de forma automática. Una forma es mediante la definición de una metaclase para su (s) clase (s) mixta (s) que crea o extiende el método __init__ la clase mixta para que invoque todas las funciones __init__ las bases enumeradas en el orden en que fueron enumeradas.

Una segunda forma de hacerlo es utilizar un decorador de clase, que se muestra a continuación en la sección subtitulada Editar .

Usando una metaclase:

class APIBaseClassOne(object): # API class (can''t be changed) def __init__(self, *args, **kwargs): print '' APIBaseClassOne.__init__()'' class SomeMixin(object): def __init__(self, *args, **kwargs): print '' SomeMixin.__init__()'' class MixedClassMeta(type): def __new__(cls, name, bases, classdict): classinit = classdict.get(''__init__'') # Possibly None. # define an __init__ function for the new class def __init__(self, *args, **kwargs): # call the __init__ functions of all the bases for base in type(self).__bases__: base.__init__(self, *args, **kwargs) # also call any __init__ function that was in the new class if classinit: classinit(self, *args, **kwargs) # add the local function to the new class classdict[''__init__''] = __init__ return type.__new__(cls, name, bases, classdict) class MixedClass(APIBaseClassOne, SomeMixin): __metaclass__ = MixedClassMeta # important # if exists, is called after the __init__''s of all the direct bases def __init__(self, *args, **kwargs): print '' MixedClass.__init__()'' print(''MixedClass():'') MixedClass()

Salida:

MixedClass(): APIBaseClassOne.__init__() SomeMixin.__init__() MixedClass.__init__()

Editar

Aquí se explica cómo lograr lo mismo con un decorador de clases (requiere Python 2.6+):

class APIBaseClassOne(object): # API class (can''t be changed) def __init__(self, *args, **kwargs): print)'' APIBaseClassOne.__init__()'') class SomeMixin(object): def __init__(self, *args, **kwargs): print('' SomeMixin.__init__()'') def mixedomatic(cls): """ Mixed-in class decorator. """ classinit = cls.__dict__.get(''__init__'') # Possibly None. # define an __init__ function for the class def __init__(self, *args, **kwargs): # call the __init__ functions of all the bases for base in cls.__bases__: base.__init__(self, *args, **kwargs) # also call any __init__ function that was in the class if classinit: classinit(self, *args, **kwargs) # make the local function the class''s __init__ setattr(cls, ''__init__'', __init__) return cls @mixedomatic class MixedClass(APIBaseClassOne, SomeMixin): # if exists, is called after the __init__''s of all the direct base classes def __init__(self, *args, **kwargs): print('' MixedClass.__init__()'') print(''MixedClass():'') MixedClass()

(Para Python <2.6, use MixedClass = mixedomatic(MixedClass) siguiendo la definición de la clase).