Hablando desuper, puede que pienses que es muy sencillo, ¿no se usa acaso para llamar métodos de la clase padre? Si realmente fuera tan simple, este artículo no existiría. Déjame explicártelo en detalle.

Convenciones

Antes de comenzar, acordemos la versión de Python que se usa en este artículo. Por defecto se usa Python 3, es decir: las clases definidas en este artículo son todas clases de nuevo estilo. Si usas Python 2, recuerda heredar de object.object:
# 默认, Python 3
class A:
    pass

# Python 2
class A(object):
    pass

Otra diferencia entre Python 3 y Python 2 es: en Python 3 se puede usar directamentesuper().xxxen lugar desuper(Class, self).xxx :

# 默认,Python 3
class B(A):
    def add(self, x):
        super().add(x)

# Python 2
class B(A):
    def add(self, x):
        super(B, self).add(x)

Por lo tanto, si usas Python 2, recuerda reemplazar elsuper()porsuepr(Class, self) 。

Si hay otras incompatibilidades con Python 2, las indicaré en el artículo.

Herencia simple

En herencia simplesuperComo todos piensan, se usa principalmente para llamar métodos de la clase padre.

class A:
    def __init__(self):
        self.n = 2

    def add(self, m):
        print('self is {0} @A.add'.format(self))
        self.n += m


class B(A):
    def __init__(self):
        self.n = 3

    def add(self, m):
        print('self is {0} @B.add'.format(self))
        super().add(m)
        self.n += 3

Después de ejecutar el siguiente código, ¿qué crees que b.nserá el valor?

b = B()
b.add(2)
print(b.n)

El resultado de la ejecución es el siguiente:

self is <__main__.B object at 0x106c49b38> @B.add
self is <__main__.B object at 0x106c49b38> @A.add
8

Este resultado ilustra dos cuestiones:

  • 1, super().add(m)Efectivamente, se llamó al método de la clase padreaddA.
  • 2, super().add(m)Al llamar al método de la clase padredef add(self, m), en ese momento, dentro de la clase padreself[self] no es una instancia de la clase padre, sino una instancia de la subclase, por lo tantob.add(2)el resultado posterior es5y no4 。

No sé si este resultado coincide con lo que pensabas. A continuación, veamos un ejemplo de herencia múltiple.

Herencia múltiple

Esta vez definamos otraclass C, unaclass D:

class C(A):
    def __init__(self):
        self.n = 4

    def add(self, m):
        print('self is {0} @C.add'.format(self))
        super().add(m)
        self.n += 4


class D(B, C):
    def __init__(self):
        self.n = 5

    def add(self, m):
        print('self is {0} @D.add'.format(self))
        super().add(m)
        self.n += 5

¿Qué imprime el siguiente código?

d = D()
d.add(2)
print(d.n)

Esta vez la salida es la siguiente:

self is <__main__.D object at 0x10ce10e48> @D.add
self is <__main__.D object at 0x10ce10e48> @B.add
self is <__main__.D object at 0x10ce10e48> @C.add
self is <__main__.D object at 0x10ce10e48> @A.add
19

¿Acertaste? Puede que pienses que la salida del código anterior es similar a:

self is <__main__.D object at 0x10ce10e48> @D.add
self is <__main__.D object at 0x10ce10e48> @B.add
self is <__main__.D object at 0x10ce10e48> @A.add
15

¿Por qué no es como se esperaba? A continuación, veremos juntossuperel misterio de super.

super es una clase

Cuando llamamos asuper(), en realidad estamos instanciando unasuperclase. No lo leíste mal,supersuper es una clase, no es una palabra clave, ni una función, ni otra estructura de datos:

>>> class A: pass
...
>>> s = super(A)
>>> type(s)
<class 'super'>
>>>

En la mayoría de los casos,supercontiene dos informaciones muy importantes: un MRO y una clase dentro del MRO. Cuando se llama de la siguiente manerasuper:

super(a_type, obj)

MROse refiere altype(obj)MRO de la instancia, y la clase dentro del MRO esa_type, al mismo tiempoisinstance(obj, a_type) == True 。

Cuando se llama así:

super(type1, type2)

el MRO se refiere altype2deMRO, MROMRO de ..., y la clase dentro del MRO estype1, al mismo tiempoissubclass(type2, type1) == True 。

Entonces,super()¿qué hace en realidad? En pocas palabras: proporciona unMROy unMROclase dentro del MROC , super()devolverá un objeto que, a partir deMROMedioClas clases posteriores a ..., busca métodos.

Es decir, la forma de búsqueda no es como el método convencional, que busca desde todas lasMROclases, sino que se busca desde laMROtail de [obj].

Por ejemplo, hay unaMRO:

[A, B, C, D, E, object]

la siguiente llamada:

super(C, A).foo()

supersolo se buscará desdeCdespués de [obj], es decir, solo seDoEoobjectbuscará enfooel método.

Cómo funciona super en la herencia múltiple

Volvamos a lo anterior

d = D()
d.add(2)
print(d.n)

Ahora quizás ya tengas una idea de por qué la salida es

self is <__main__.D object at 0x10ce10e48> @D.add
self is <__main__.D object at 0x10ce10e48> @B.add
self is <__main__.D object at 0x10ce10e48> @C.add
self is <__main__.D object at 0x10ce10e48> @A.add
19

¿verdad? ;)

A continuación, analicémoslo en detalle:

  • DdeMROes:[D, B, C, A, object] 。 Nota:Se puede, medianteD.mro()(en Python 2, se usaD.__mro__) consultarDdeMROla información)

  • El análisis detallado del código es el siguiente:

    class A:
        def __init__(self):
            self.n = 2
    
        def add(self, m):
            # 第四步
            # 来自 D.add 中的 super
            # self == d, self.n == d.n == 5
            print('self is {0} @A.add'.format(self))
            self.n += m
            # d.n == 7
    
    
    class B(A):
        def __init__(self):
            self.n = 3
    
        def add(self, m):
            # 第二步
            # 来自 D.add 中的 super
            # self == d, self.n == d.n == 5
            print('self is {0} @B.add'.format(self))
            # 等价于 suepr(B, self).add(m)
            # self 的 MRO 是 [D, B, C, A, object]
            # 从 B 之后的 [C, A, object] 中查找 add 方法
            super().add(m)
    
            # 第六步
            # d.n = 11
            self.n += 3
            # d.n = 14
    
    class C(A):
        def __init__(self):
            self.n = 4
    
        def add(self, m):
            # 第三步
            # 来自 B.add 中的 super
            # self == d, self.n == d.n == 5
            print('self is {0} @C.add'.format(self))
            # 等价于 suepr(C, self).add(m)
            # self 的 MRO 是 [D, B, C, A, object]
            # 从 C 之后的 [A, object] 中查找 add 方法
            super().add(m)
    
            # 第五步
            # d.n = 7
            self.n += 4
            # d.n = 11
    
    
    class D(B, C):
        def __init__(self):
            self.n = 5
    
        def add(self, m):
            # 第一步
            print('self is {0} @D.add'.format(self))
            # 等价于 super(D, self).add(m)
            # self 的 MRO 是 [D, B, C, A, object]
            # 从 D 之后的 [B, C, A, object] 中查找 add 方法
            super().add(m)
    
            # 第七步
            # d.n = 14
            self.n += 5
            # self.n = 19
    
    d = D()
    d.add(2)
    print(d.n)

    El diagrama del proceso de llamada es el siguiente:

    D.mro() == [D, B, C, A, object]
    d = D()
    d.n == 5
    d.add(2)
    
    class D(B, C):          class B(A):            class C(A):             class A:
        def add(self, m):       def add(self, m):      def add(self, m):       def add(self, m):
            super().add(m)  1.--->  super().add(m) 2.--->  super().add(m)  3.--->  self.n += m
            self.n += 5   <------6. self.n += 3    <----5. self.n += 4     <----4. <--|
            (14+5=19)               (11+3=14)              (7+4=11)                (5+2=7)

Ahora ya sabes por qué, después de d.add(2), el valor de d.n es 19, ¿verdad? ;)

Fuente del artículo: https://mozillazg.com/2016/12/python-super-is-not-as-simple-as-you-thought.html