Sont Mixin classe de __init__ fonctions qui ne sont pas automatiquement appelé?
Je voudrais utiliser un Mixin de toujours ajouter quelques init fonctionnalité à mon enfant des classes qui chaque hériter de différentes API des classes de base. Plus précisément, je voudrais faire plusieurs enfants des classes qui héritent de l'un de ces différentes API fourni par les classes de base et l'un Mixin, qui aura toujours le Mixin code d'initialisation exécutée de la même manière, sans code de la réplication. Cependant, il semble que le __init__ de la fonction de la classe Mixin n'est jamais appelé, sauf si j'ai choisi de l'appeler dans l'Enfant de la classe de __init__ de la fonction, ce qui est loin d'être idéale. J'ai fait un test simple cas:
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
Comme vous pouvez le voir dans la sortie suivante, le Mixin de la fonction init n'est jamais appelé:
>>> import test
>>> test.MixedClass()
base
<test.MixedClass object at 0x1004cc850>
Est-il un moyen de le faire (avoir une fonction init dans un Mixin appelée), sans écrire de chaque enfant de la classe pour appeler explicitement le Mixin de la fonction init? (c'est à dire, sans avoir à faire quelque chose de ce genre dans chaque classe:)
class MixedClass(MyClass, SomeMixin):
def __init__(*args, **kwargs):
SomeMixin.__init__(self, *args, **kwargs)
MyClass.__init__(self, *args, **kwargs)
Btw, si tous mes enfants les classes héritant de la même classe de base, je me rends compte que je pouvais créer une nouvelle classe moyenne, qui hérite de la classe de base et le mixin et le garder au SEC de cette façon. Cependant, ils héritent de différentes classes de base avec des fonctionnalités communes. (Django Champ classes, pour être précis).
- En général, l'utilisation de l'héritage multiple avec des classes de base qui n'étaient pas conçues pour cela est une mauvaise idée. Mélanger les classes sont généralement conçus ensemble, et de mélange arbitraire des classes de produit de tels dégâts. En tout cas, si les deux classes de base ont chacun une
__init__
méthode, comment l'interprète de savoir qui de l'un à l'appel, ou l'ordre dans lequel les appeler? - Caron: Il est possible de déterminer l'ordre comme le C++ n', où les classes de base sont initialisés dans l'ordre de déclaration.
Vous devez vous connecter pour publier un commentaire.
Désolé, j'ai vu cela si tard, mais
Le modèle @Ignacio parle est appelé coopérative de l'héritage multiple, et c'est génial. Mais si une classe de base n'est pas intéressé par une collaboration, elle est la deuxième base, et votre mixin la première. Le mixin est
__init__()
(et rien d'autre il définit) seront vérifiés avant la classe de base, à la suite de Python MRO.Ce qui devrait résoudre la question générale, si je ne suis pas sûr qu'il s'occupe de votre utilisation spécifique. Les classes de Base personnalisée avec des metaclasses (comme Django modèles) ou avec d'étranges décorateurs (comme @martineau réponse 😉 peut faire des choses folles.
Ont la classe de base invoquer
super().__init__()
même si c'est une sous-classe deobject
. De cette façon, tous les__init__()
des méthodes à exécuter.super()
pour trouver la prochaine classe à initialiser.BaseClassOne
parce qu'il part d'une API qu'ils n'ont aucun contrôle sur.__init__
avec unsuper(...).__init__
appel à deux l'OPMyClass
etMixedClass
mais l'un pourSomeMixin
est toujours pas appelé lorsque j'instancie unMixedClass
objet -- donc je suis peut-être l'incompréhension de ce que vous êtes en train de suggérer.Child(Mixin1, Mixin2):
, si vous voulez appeler les parents de init vous devriez les appeler. (super(Mixin1, self).__init__()
etsuper(Mixin2,self).__init__()
) sinon, python va appeler le premier (Mixin1)Python n'effectue aucune implicite des appels à la
__init__
les méthodes d'une classe de super-classe(s)—mais il est possible de le faire automatiquement. Une façon est par définition une métaclasse pour votre classe mixte(es) qui crée ou s'étend la classe mixte'__init__
méthode de manière à ce qu'il appelle tous les bases'__init__
fonctions dans l'ordre où ils ont été répertoriés.La deuxième façon est de le faire est d'utiliser une classe décorateur—qui est montré dans la Modifier section ci-dessous.
À l'aide d'une métaclasse:
De sortie:
Modifier
Voici comment réaliser la même chose avec une classe décorateur (nécessite la version 2.6 de Python+):
Notes
Pour Python < 2.6, utilisez
MixedClass = mixedomatic(MixedClass)
suivantes la définition de la classe.En Python 3 la syntaxe pour spécifier des metaclasses est différent, donc au lieu de l':
indiqué ci-dessus, vous devez utiliser:
La classe décorateur version fonctionnera dans les deux versions.
init()
. Problèmes du monde réel peut être comme ça.classinit = cls.__init__ if '__init__' in cls.__dict__ else None
classinit = getattr(cls, '__init__', None)
?classinit = getattr(cls, '__init__', None)
toujours là ou était-ce un montage? Je pense que peut-être il l'habitude d'être justeclassinit = cls.__init__
j'ai donc proposé la modification, mais à l'aide de getattr est probablement équivalent. Je l'utilise ici: _mixin_common.py et voilà optimizers.pySomeCls
n'a pas de__init__
, mais à l'aide degetattr
de produire un init b/c SomeMixin et/ou SomeBase peut avoir un init. Ma suggestion serait d'identifier correctement que SomeCls n'a pas de init. - Je l'utiliser dans optimizers.py je l'ai mentionné ci-dessus.classinit = getattr(cls, '__init__', None)
produit exactement le même résultat que ce que votreif
instruction, mais de façon plus succincte. Deuxièmement, lamixedomatic()
décorateur toujours crée un__init__()
pour la salle de classe et laclassinit
variable est utilisée pour déterminer si la création de la méthode doit appeler tout__init__()
qui pourrait avoir existé déjà lorsqu'elle est appelée (à la suite d'appels à celles de tous les directe les classes de base).SomeMixin.__init__
deux fois. C'est un bug.getattr()
ne retourne pas la même chose que leif
-in
déclaration dans le cas d'une classe décorateur, car ceux-ci sont exécutés après un premier version non modifiée de la classe a déjà été créé etgetattr()
suit la MRO de la classe temporaire et renvoie le__init__
dans le premier match de la hiérarchie d'héritage (le premier de la classe de base__init__()
ici)...