2010-09-28 24 views

Répondre

8

super() résoudra jamais qu'un seul type de classe pour une méthode donnée, donc si vous êtes héritant de plusieurs classes et que vous voulez appeler la méthode dans les deux, vous » ll faut le faire explicitement.

+0

Merci pour la réponse. Maintenant je ne me sens plus sale pour ne pas utiliser super(). – sayap

+4

Lorsque vous appelez explicitement les méthodes de classe de base * can * pour des scénarios très simples comme l'exemple du questionneur, il se décomposera si les classes de base héritent d'une base commune et que vous ne souhaitez pas appeler la méthode . Ceci est connu comme "l'héritage du diamant" et c'est un gros problème pour de nombreux systèmes d'héritage multiple (comme en C++). L'héritage multiple collaboratif de Python (avec 'super()') vous permet de le résoudre facilement dans de nombreux cas (même si cela ne veut pas dire qu'une hiérarchie coopérative d'héritage multiple est facile à concevoir ou toujours une bonne idée). – Blckknght

4

Si vous avez ajouté les éléments suivants:

class A(object): 
    def foo(self): 
     print 'A.foo()' 

class B(object): 
    def foo(self): 
     print 'B.foo()' 

class C(A, B): 
    def foo(self): 
     super(C, self).foo() 
     print 'C.foo()' 
     A.foo(self) 
     B.foo(self) 


c = C() 
c.foo() 

Ensuite super (C, auto) .foo() fait référence à A.foo

La sortie est

A.foo() 
C.foo() 
A.foo() 
B.foo() 

[Modifier: Inclure des informations et des liens supplémentaires]

+0

Oui, c'était ma question. Donc, super n'est pas destiné à ce cas? – sayap

+0

@sayap: J'ai édité ma réponse pour inclure des liens qui sont pertinents pour comprendre comment fonctionne super. Cela fonctionne mais il trouvera d'abord A.foo et si A.foo n'était pas là, B.foo aurait été examiné. – pyfunc

+0

Pourquoi voudrais-je appeler A.foo() deux fois? – sayap

6

Super appellera la méthode foo sur la "première" super classe. Ceci est basé sur l'ordre de résolution de méthode (__mro__) pour la classe C.

>>> C.__mro__ 
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>) 
>>> 

Par conséquent, si vous appelez super(C, self).foo(), A.foo est appelé. Si vous modifiez l'ordre d'héritage à class C(B, A):, cela est inversé. Le __mro__ ressemble maintenant:

>>> C.__mro__ 
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>) 
>>> 

Si vous appelez super(C, self).foo() après avoir fait ce changement, B.foo() sera appelé.

+0

yay pour l'introspection –

19

super est en effet destiné à cette situation, mais cela ne fonctionne que si vous l'utilisez de manière cohérente. Si les classes de base n'utilisent pas toutes super cela ne fonctionnera pas, et à moins que la méthode soit dans object vous devez utiliser quelque chose comme une classe de base commune pour terminer la chaîne des appels super.

class FooBase(object): 
    def foo(self): pass 

class A(FooBase): 
    def foo(self): 
     super(A, self).foo() 
     print 'A.foo()' 

class B(FooBase): 
    def foo(self): 
     super(B, self).foo() 
     print 'B.foo()' 

class C(A, B): 
    def foo(self): 
     super(C, self).foo() 
     print 'C.foo()' 

@Marcin demande pourquoi il doit y avoir une base commune:

Sans FooBase qui implémente foo mais ne remet pas super() la dernière classe qui n'appelle super() obtiendra une erreur d'attribut car il n'y a méthode de base à appeler.

S'il y avait des classes de base séparées class A(AFooBase): et class B(BFooBase): l'appel super() en A appellerait la méthode AFooBase et la méthode B ne serait jamais appelé. Lorsque la base est commune à toutes les classes, elle va à la fin de l'ordre de résolution de la méthode et vous pouvez être certain que peu importe comment les classes sont définies, la méthode de la classe de base sera la dernière appelée.

+0

Je pense que c'est une réponse beaucoup plus informative que celle acceptée. Pourriez-vous développer pourquoi on a besoin de la classe de base commune pour faire ce travail? – Marcin

+0

@marcin Vous n'avez pas besoin d'une base commune pour faire fonctionner super(). @duncan démontre simplement comment super() est encore meilleur quand il s'agit de l'héritage du diamant. Et en passant, la sortie de l'appel 'C(). Foo()' sera 'B.foo() A.foo(). C.foo() 'plutôt que" A .. B .. C .. "ordre, et la raison est MRO expliquant dans [la réponse de Manoj] (http://stackoverflow.com/a/3810465/728675) – RayLuo

+0

@RayLuo Cette réponse dit littéralement qu'une base commune est nécessaire – Marcin

0

Ceci est possible avec super

class A(object): 
    def foo(self): 
     print 'A.foo()' 

class B(object): 
    def foo(self): 
     print 'B.foo()' 

class C(A, B): 
    def foo(self): 
     print 'C.foo()' 
     super(C, self).foo() # calls A.foo() 
     super(A, self).foo() # calls B.foo() 
+0

Alors qu'est-ce que vous gagnez en appelant 'super (C, self) .foo()' ou 'super (A, self) .foo()' au lieu de 'A.foo()' ou 'B.foo()' ? – FooF

1

Comme Duncan dit ci-dessus, vous pouvez obtenir des résultats prévisibles, surtout si vous utilisez super() cohérente.

Les résultats de l'essai suivant ont été utiles pour moi:

class FooBase(object): 
    def foo(self): 
     print 'FooBase.foo()' 

class A(FooBase): 
    def foo(self): 
     print 'A.foo() before super()' 
     super(A, self).foo() 
     print 'A.foo() after super()' 

class B(FooBase): 
    def foo(self): 
     print 'B.foo() before super()' 
     super(B, self).foo() 
     print 'B.foo() after super()' 

class C(A, B): 
    def foo(self): 
     print 'C.foo() before super()' 
     super(C, self).foo() 
     print 'C.foo() after super()' 

Cette imprimera:

>>> c = C() 
>>> c.foo() 
C.foo() before super() 
A.foo() before super() 
B.foo() before super() 
FooBase.foo() 
B.foo() after super() 
A.foo() after super() 
C.foo() after super() 
7

Merci pour tous ceux qui ont contribué à ce fil. Pour résumer:

  • The (currently) accepted answer est inexacte. La description correcte devrait être: super() n'est pas seulement bon pour la résolution de l'héritage unique, MAIS aussi l'héritage multiple. Et la raison est bien expliqué dans le commentaire de l » @blckknght:

    Tout en appelant explicitement les méthodes de la classe de base peut fonctionner pour des scénarios très simples comme l'exemple de l'interrogateur, elle se décomposera si les classes de base elles-mêmes héritent d'un commun base et vous ne voulez pas que la méthode de la classe de base ultime soit appelée deux fois. Ceci est connu comme "l'héritage du diamant" et c'est un gros problème pour de nombreux systèmes d'héritage multiple (comme en C++). L'héritage multiple collaboratif de Python (avec super()) vous permet de le résoudre facilement dans de nombreux cas (même si cela ne veut pas dire qu'une hiérarchie coopérative d'héritage multiple est facile à concevoir ou toujours une bonne idée).

  • La bonne façon, comme @duncan pointed out, est d'utiliser super(), mais l'utiliser de manière cohérente.

    super est en effet destiné à cette situation, mais il ne fonctionne que si vous l'utilisez régulièrement. Si les classes de base n'utilisent pas toutes super cela ne fonctionnera pas, et à moins que la méthode soit dans object vous devez utiliser quelque chose comme une classe de base commune pour terminer la chaîne des appels super.

    class FooBase(object): 
        def foo(self): pass 
    
    class A(FooBase): 
        def foo(self): 
         super(A, self).foo() 
         print 'A.foo()' 
    
    class B(FooBase): 
        def foo(self): 
         super(B, self).foo() 
         print 'B.foo()' 
    
    class C(A, B): 
        def foo(self): 
         super(C, self).foo() 
         print 'C.foo()' 
    
    C().foo() # Run this 
    

    Mais il est également intéressant de souligner que, la méthode pour appeler peut ne pas sembler intuitive. Le résultat est:

    B.foo() 
    A.foo() 
    C.foo() 
    

    Et la raison de cet ordre apparemment étrange est basé sur MRO. As @Manoj-Govindan pointed out,

    super() appellera la méthode foo sur la "première" super classe. Ceci est basé sur l'ordre de résolution de méthode (__mro__) pour la classe C.

    >>> C.__mro__ 
    (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>) 
    >>> 
    
  • En règle générale, si vous voulez voyager à toutes les méthodes de parents, mais ne se soucient pas vraiment l'ordre Invoke, utilisez super() consisently. Sinon, vous pouvez choisir d'appeler explicitement les méthodes parentes dans un ordre spécifique.Ne pas mélanger l'utilisation de super() et des appels explicites bien que. Sinon, vous finirez par duplication méchant comme mentionné dans this answer.

PS: J'ai augmenté la plupart des sources mentionnées ci-dessus.