2010-09-28 15 views

Respuesta

8

super() nunca dejará de resolver un solo tipo de clase de un método dado, así que si estás heredando de múltiples clases y quiere llamar al método de los dos, que' tendré que hacerlo explícitamente

+0

Gracias por la respuesta. Ahora ya no me siento sucio por no usar super(). – sayap

+4

Si bien llamar explícitamente a los métodos de clase base * puede * funcionar para escenarios muy simples como el ejemplo del cuestionario, se descompondrá si las clases base heredan de una base común y no desea que se llame dos veces al método de la clase base definitiva . Esto se conoce como "herencia de diamantes" y es un gran problema para muchos sistemas de herencia múltiple (como en C++). La herencia múltiple colaborativa de Python (con 'super()') le permite resolverla fácilmente en muchos casos (aunque eso no quiere decir que una jerarquía de herencia múltiple cooperativa sea fácil de diseñar o siempre sea una buena idea). – Blckknght

4

Si añade lo siguiente:

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

Entonces super (C, la auto) .foo() se refiere a A.foo

La salida es

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

[Editar: Incluir información y enlaces adicionales]

+0

Sí, esa era mi pregunta. ¿Tan súper no es para este caso? – sayap

+0

@sayap: He editado mi respuesta para incluir enlaces que sean relevantes para comprender cómo funciona Super. Funciona, pero primero encontrará a A.foo y si A.foo no estaba allí, entonces B.foo habría sido investigado. – pyfunc

+0

¿Por qué querría que A.foo() llamara dos veces? – sayap

6

Super llamará al método foo en la "primera" superclase. Esto se basa en la Orden de resolución de método (__mro__) para la clase C.

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

Por lo tanto si se llama a super(C, self).foo(), A.foo se llama. Si cambia la orden de herencia a class C(B, A):, se invierte. El __mro__ se ve ahora como:

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

Si llama super(C, self).foo() después de hacer este cambio, B.foo() será llamado.

+0

yay por introspección –

19

super está diseñado para esta situación, pero solo funciona si lo usa de forma coherente. Si las clases base tampoco usan super, no funcionará, y a menos que el método esté en object, debe usar algo así como una clase base común para finalizar la cadena de llamadas 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 pregunta por qué tiene que haber una base común:

Sin FooBase que implementa foo pero no llama super() la última clase que llaman super() obtendrá un error atributo ya que no hay método base para llamar.

Si hubo clases base separadas class A(AFooBase): y class B(BFooBase): la llamada super() en A llamaría el método en el AFooBase y el método de B nunca ser llamado. Cuando la base es común a todas las clases, va al final del orden de resolución del método y puede estar seguro de que no importa cómo se definan las clases, el método de clase base será el último llamado.

+0

Creo que esta es una respuesta mucho más informativa que la aceptada. ¿Podría ampliar en por qué uno necesita la clase base común para que esto funcione? – Marcin

+0

@marcin No necesita una base común para hacer funcionar a super(). @duncan simplemente está demostrando cómo super() es incluso mejor cuando se trata de herencia de diamantes. Y, por cierto, la salida de la llamada 'C(). Foo()' será 'B.foo() A.foo(). C.foo() 'en lugar de" A .. B .. C .. "orden, y la razón es MRO explicando en [la respuesta de Manoj] (http://stackoverflow.com/a/3810465/728675) – RayLuo

+0

@RayLuo Esta respuesta literalmente dice que se necesita una base común – Marcin

0

Esto es posible con 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

Entonces, ¿qué ganas al llamar 'super (C, self) .foo()' o 'super (A, self) .foo()' en lugar de 'A.foo()' o 'B.foo()' ? – FooF

1

Como dijo Duncan anteriormente, se pueden obtener resultados predecibles especialmente si se utiliza super() de forma coherente.

Los resultados de la prueba siguiente fueron útiles para mí:

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

Esto imprimirá:

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

Gracias por todos aquellos contribuido a este hilo. Para resumir:

  • The (currently) accepted answer es inexacta. La descripción correcta debería ser: super() NO SÓLO es bueno para resolver herencia individual, PERO TAMBIÉN herencias múltiples. Y la razón está bien explicado en el comentario @blckknght 's:

    Mientras una llamada explícita a los métodos de la clase base puede funcionar para escenarios muy simples como el ejemplo de la pregunta, se descompone si las clases de base en sí heredan de una común base y no quiere que el método de la última clase base sea llamado dos veces. Esto se conoce como "herencia de diamantes" y es un gran problema para muchos sistemas de herencia múltiple (como en C++). La herencia múltiple colaborativa de Python (con super()) le permite resolverla fácilmente en muchos casos (aunque eso no quiere decir que una jerarquía de herencia múltiple cooperativa sea fácil de diseñar o siempre una buena idea).

  • La manera correcta, como @duncan pointed out, es usar super(), pero úselo consistentemente.

    super está diseñado para esta situación, pero solo funciona si lo usa de forma coherente. Si las clases base tampoco usan super, no funcionará, y a menos que el método esté en object, debe usar algo así como una clase base común para finalizar la cadena de llamadas 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 
    

    Pero es también vale la pena señalar que, el orden llamando al método puede no parecer intuitivo. El resultado es:

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

    Y el motivo de este orden aparentemente extraño se basa en MRO. As @Manoj-Govindan pointed out,

    super() llamará al método foo en la "primera" superclase. Esto se basa en la Orden de resolución de método (__mro__) para la clase C.

    >>> C.__mro__ 
    (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>) 
    >>> 
    
  • Como regla general, si desea viajar de regreso a todos los métodos de los padres, pero no le importa realmente el orden de invocación, utiliza super() consisently. De lo contrario, puede optar por llamar explícitamente a los métodos principales en un orden específico.

  • No mezcle el uso de super() y llamadas explícitas. De lo contrario, terminará duplicación desagradable como se menciona en this answer.

PD: He votado la mayoría de las fuentes mencionadas anteriormente.

Cuestiones relacionadas