Que fait 'super' en Python?


Answers

Quelle est la différence?

SomeBaseClass.__init__(self) 

signifie appeler le SomeBaseClass de __init__ . tandis que

super(Child, self).__init__()

signifie d'appeler un __init__ lié de la classe parent qui suit Child dans l'ordre de résolution de la méthode de l'instance.

Si l'instance est une sous-classe de Child, il se peut qu'un autre parent apparaisse dans l'ordre de résolution de la méthode.

Python 2 contre 3

Cela fonctionne dans Python 2 et 3:

super(Child, self).__init__()

Cela ne fonctionne que dans Python 3:

super().__init__()

Il fonctionne sans arguments en remontant dans le cadre de la pile et en obtenant le premier argument de la méthode (habituellement self pour une méthode d'instance ou cls pour une méthode de classe - mais peut être d'autres noms) et en trouvant la classe variables libres (il est recherché avec le nom __class__ comme variable de fermeture libre dans la méthode).

Je préfère démontrer la façon cross-compatible d'utiliser super , mais si vous n'utilisez que Python 3, vous pouvez l'appeler sans arguments.

Indirection avec la compatibilité ascendante

Qu'est-ce que ça te donne? Pour l'héritage unique, les exemples de la question sont pratiquement identiques du point de vue de l'analyse statique. Cependant, l'utilisation de super vous donne une couche d'indirection avec compatibilité ascendante.

La compatibilité ascendante est très importante pour les développeurs chevronnés. Vous voulez que votre code continue de fonctionner avec un minimum de modifications lorsque vous le modifiez. Quand vous regardez votre historique de révision, vous voulez voir précisément ce qui a changé quand.

Vous pouvez commencer avec un héritage simple, mais si vous décidez d'ajouter une autre classe de base, vous n'avez qu'à changer la ligne avec les bases - si les bases changent dans une classe dont vous héritez (ajoutez un mixin) vous changeriez rien dans cette classe. En particulier dans Python 2, il peut être difficile d'obtenir les arguments super et les bons arguments de méthode. Si vous savez que vous utilisez super correctement avec l'héritage unique, cela rend le débogage moins difficile à l'avenir.

Injection de dépendance

D'autres personnes peuvent utiliser votre code et injecter des parents dans la résolution de la méthode:

class SomeBaseClass(object):
    def __init__(self):
        print('SomeBaseClass.__init__(self) called')

class UnsuperChild(SomeBaseClass):
    def __init__(self):
        print('UnsuperChild.__init__(self) called')
        SomeBaseClass.__init__(self)

class SuperChild(SomeBaseClass):
    def __init__(self):
        print('SuperChild.__init__(self) called')
        super(SuperChild, self).__init__()

Supposons que vous ajoutez une autre classe à votre objet et que vous souhaitiez injecter une classe entre Foo et Bar (à des fins de test ou pour toute autre raison):

class InjectMe(SomeBaseClass):
    def __init__(self):
        print('InjectMe.__init__(self) called')
        super(InjectMe, self).__init__()

class UnsuperInjector(UnsuperChild, InjectMe): pass

class SuperInjector(SuperChild, InjectMe): pass

L'utilisation de l'enfant non super n'injecte pas la dépendance car l'enfant que vous utilisez a codé en dur la méthode qui doit être appelée après la sienne:

>>> o = UnsuperInjector()
UnsuperChild.__init__(self) called
SomeBaseClass.__init__(self) called

Cependant, la classe avec l'enfant qui utilise super peut correctement injecter la dépendance:

>>> o2 = SuperInjector()
SuperChild.__init__(self) called
InjectMe.__init__(self) called
SomeBaseClass.__init__(self) called

Conclusion

Utilisez toujours super pour référencer la classe parent.

Ce que vous avez l'intention de faire, c'est de référencer la classe parente qui est en ligne, pas spécifiquement celle dont vous voyez l'enfant hériter.

Ne pas utiliser super peut imposer des contraintes inutiles aux utilisateurs de votre code.

Question

Quelle est la différence entre:

class Child(SomeBaseClass):
    def __init__(self):
        super(Child, self).__init__()

et:

class Child(SomeBaseClass):
    def __init__(self):
        SomeBaseClass.__init__(self)

J'ai vu super être utilisé beaucoup dans les classes avec un seul héritage. Je peux voir pourquoi vous l'utiliseriez dans l'héritage multiple mais je ne sais pas quels sont les avantages de l'utiliser dans ce genre de situation.




J'avais joué un peu avec super() , et avais reconnu que nous pouvions changer d'ordre d'appel.

Par exemple, nous avons la structure de la hiérarchie suivante:

    A
   / \
  B   C
   \ /
    D

Dans ce cas MRO de D sera (uniquement pour Python 3):

In [26]: D.__mro__
Out[26]: (__main__.D, __main__.B, __main__.C, __main__.A, object)

Créons une classe où super() appelle après l'exécution de la méthode.

In [23]: class A(object): #  or with Python 3 can define class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          print("I'm from B")
...:          super().__init__()
...:   
...: class C(A):
...:      def __init__(self):
...:          print("I'm from C")
...:          super().__init__()
...:  
...: class D(B, C):
...:      def __init__(self):
...:          print("I'm from D")
...:          super().__init__()
...: d = D()
...:
I'm from D
I'm from B
I'm from C
I'm from A

    A
   / ⇖
  B ⇒ C
   ⇖ /
    D

Nous pouvons donc voir que l'ordre de résolution est le même que dans MRO. Mais quand nous appelons super() au début de la méthode:

In [21]: class A(object):  # or class A:
...:     def __init__(self):
...:         print("I'm from A")
...:  
...: class B(A):
...:      def __init__(self):
...:          super().__init__()  # or super(B, self).__init_()
...:          print("I'm from B")
...:   
...: class C(A):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from C")
...:  
...: class D(B, C):
...:      def __init__(self):
...:          super().__init__()
...:          print("I'm from D")
...: d = D()
...: 
I'm from A
I'm from C
I'm from B
I'm from D

Nous avons un ordre différent, il est inversé un ordre du tuple MRO.

    A
   / ⇘
  B ⇐ C
   ⇘ /
    D 

Pour une lecture supplémentaire, je recommanderais les réponses suivantes:

  1. Exemple de linéarisation C3 avec super (une grande hiérarchie)
  2. Changements de comportement importants entre les anciennes et les nouvelles classes de style
  3. L'histoire intérieure sur les classes de style nouveau