español - django python 3
¿Cómo hace el "súper" de Python lo correcto? (5)
Cambia tu código a esto y creo que explicará las cosas (presumiblemente super
está mirando dónde, por ejemplo, B
está en el __mro__
?):
class A(object):
def __init__(self):
print "A init"
print self.__class__.__mro__
class B(A):
def __init__(self):
print "B init"
print self.__class__.__mro__
super(B, self).__init__()
class C(A):
def __init__(self):
print "C init"
print self.__class__.__mro__
super(C, self).__init__()
class D(B, C):
def __init__(self):
print "D init"
print self.__class__.__mro__
super(D, self).__init__()
x = D()
Si lo ejecuta, verá:
D init
(<class ''__main__.D''>, <class ''__main__.B''>, <class ''__main__.C''>, <class ''__main__.A''>, <type ''object''>)
B init
(<class ''__main__.D''>, <class ''__main__.B''>, <class ''__main__.C''>, <class ''__main__.A''>, <type ''object''>)
C init
(<class ''__main__.D''>, <class ''__main__.B''>, <class ''__main__.C''>, <class ''__main__.A''>, <type ''object''>)
A init
(<class ''__main__.D''>, <class ''__main__.B''>, <class ''__main__.C''>, <class ''__main__.A''>, <type ''object''>)
También vale la pena comprobar que el Super de Python es ingenioso, pero no puedes usarlo .
Estoy ejecutando Python 2.5, por lo que esta pregunta puede no aplicarse a Python 3. Cuando crea una jerarquía de clase de diamante usando herencia múltiple y crea un objeto de la clase derivada, Python hace lo correcto (TM). Llama al constructor para la clase derivada, luego a sus clases principales enumeradas de izquierda a derecha, y luego a los abuelos. Estoy familiarizado con el MRO de Python; esa no es mi pregunta. Tengo curiosidad de cómo el objeto devuelto de súper en realidad se las arregla para comunicarse con las llamadas de super en las clases principales en el orden correcto. Considere este código de ejemplo:
#!/usr/bin/python
class A(object):
def __init__(self): print "A init"
class B(A):
def __init__(self):
print "B init"
super(B, self).__init__()
class C(A):
def __init__(self):
print "C init"
super(C, self).__init__()
class D(B, C):
def __init__(self):
print "D init"
super(D, self).__init__()
x = D()
El código hace lo intuitivo, imprime:
D init
B init
C init
A init
Sin embargo, si comenta la llamada a super en la función init de B, no se llama ni a la función init de A ni de C. Esto significa que la llamada de B a super es de algún modo consciente de la existencia de C en la jerarquía de clases general. Sé que super devuelve un objeto proxy con un operador get sobrecargado, pero ¿cómo el objeto devuelto por super en la definición de inicio de D''comunica la existencia de C al objeto devuelto por super en la definición de inicio de B? ¿La información que las llamadas subsiguientes de superusuario almacenan en el objeto mismo? Si es así, ¿por qué no es super en su lugar self.super?
Editar: Jekke señaló con toda razón que no es self.super porque super es un atributo de la clase, no una instancia de la clase. Conceptualmente, esto tiene sentido, ¡pero en la práctica el súper tampoco es un atributo de la clase! Puede probar esto en el intérprete haciendo dos clases A y B, donde B hereda de A, y llama a dir(B)
. No tiene atributos super
o __super__
.
He proporcionado varios enlaces a continuación, que responden a su pregunta con más detalle y con más precisión de lo que espero. Sin embargo, daré una respuesta a su pregunta en mis propias palabras, para ahorrarle algo de tiempo. Lo pondré en puntos -
- super es una función incorporada, no un atributo.
- Cada tipo (clase) en Python tiene un atributo
__mro__
, que almacena el orden de resolución del método de esa instancia particular. - Cada llamada a super es de la forma super (tipo [, objeto-o-tipo]). Supongamos que el segundo atributo es un objeto por el momento.
- En el punto de partida de las llamadas súper, el objeto es del tipo de la clase derivada (por ejemplo, DC ).
- súper busca métodos que coincidan (en su caso
__init__
) en las clases en el MRO, después de la clase especificada como el primer argumento (en este caso, clases después de DC). - Cuando se encuentra el método de coincidencia (por ejemplo, en la clase BC1 ), se llama.
(Este método debe usar super, así que supongo que sí - Ver el super de Python es ingenioso pero no se puede usar - enlace a continuación) Ese método provoca una búsqueda en el MRO de la clase del objeto para el siguiente método, a la derecha de BC1 . - Enjuague la repetición de lavado hasta encontrar y llamar a todos los métodos.
Explicación para su ejemplo
MRO: D,B,C,A,object
- se llama a
super(D, self).__init__()
. isinstance (self, D) => True Busque el siguiente método en el MRO en las clases a la derecha de D.
B.__init__
encontrado y llamadoB.__init__
llama asuper(B, self).__init__()
.isinstance (self, B) => False
isinstance (self, D) => TruePor lo tanto, el MRO es el mismo, pero la búsqueda continúa a la derecha de B, es decir, C, A, se buscan objetos uno por uno. El siguiente
__init__
encontrado se llama.Y así sucesivamente y así sucesivamente.
Una explicación de super
http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
Cosas a tener en cuenta cuando se usa super
http://fuhm.net/super-harmful/
Algoritmo MRO de pitones:
http://www.python.org/download/releases/2.3/mro/
super''s docs:
http://docs.python.org/library/functions.html
La parte inferior de esta página tiene una buena sección en super:
http://docstore.mik.ua/orelly/other/python/0596001886_pythonian-chp-5-sect-2.html
Espero que esto ayude a aclararlo.
solo adivinando:
self
en los cuatro métodos se refieren al mismo objeto, es decir, de clase D
entonces, en B.__init__()
, la llamada a super(B,self)
conoce toda la ascendencia de diamante de self
y tiene que buscar el método desde ''after'' B
en este caso, es la clase C
super()
conoce la jerarquía de clase completa . Esto es lo que sucede dentro de init de B:
>>> super(B, self)
<super: <class ''B''>, <D object>>
Esto resuelve la pregunta central,
¿cómo el objeto devuelto por super en la definición de inicio de D comunica la existencia de C al objeto devuelto por super en la definición de inicio de B?
A saber, en la definición de inicio de B, el self
es una instancia de D
y, por lo tanto, comunica la existencia de C
Por ejemplo, C
se puede encontrar en type(self).__mro__
.
La respuesta de Jacob muestra cómo entender el problema, mientras que Batbrat muestra los detalles y hrr va directo al grano.
Una cosa que no cubren (al menos no explícitamente) de su pregunta es este punto:
Sin embargo, si comenta la llamada a super en la función init de B, no se llama ni a la función init de A ni de C.
Para entender eso, cambie el código de Jacob para imprimir la pila en el inicio de A, como se muestra a continuación:
import traceback
class A(object):
def __init__(self):
print "A init"
print self.__class__.__mro__
traceback.print_stack()
class B(A):
def __init__(self):
print "B init"
print self.__class__.__mro__
super(B, self).__init__()
class C(A):
def __init__(self):
print "C init"
print self.__class__.__mro__
super(C, self).__init__()
class D(B, C):
def __init__(self):
print "D init"
print self.__class__.__mro__
super(D, self).__init__()
x = D()
Es un poco sorprendente ver que la línea de super(B, self).__init__()
realidad está llamando a C.__init__()
, ya que C
no es una clase base de B
D init
(<class ''__main__.D''>, <class ''__main__.B''>, <class ''__main__.C''>, <class ''__main__.A''>, <type ''object''>)
B init
(<class ''__main__.D''>, <class ''__main__.B''>, <class ''__main__.C''>, <class ''__main__.A''>, <type ''object''>)
C init
(<class ''__main__.D''>, <class ''__main__.B''>, <class ''__main__.C''>, <class ''__main__.A''>, <type ''object''>)
A init
(<class ''__main__.D''>, <class ''__main__.B''>, <class ''__main__.C''>, <class ''__main__.A''>, <type ''object''>)
File "/tmp/jacobs.py", line 31, in <module>
x = D()
File "/tmp/jacobs.py", line 29, in __init__
super(D, self).__init__()
File "/tmp/jacobs.py", line 17, in __init__
super(B, self).__init__()
File "/tmp/jacobs.py", line 23, in __init__
super(C, self).__init__()
File "/tmp/jacobs.py", line 11, in __init__
traceback.print_stack()
Esto sucede porque super (B, self)
no está '' llamando a la versión de __init__
base B de __init__
''. En cambio, está '' invocando __init__
en la primera clase a la derecha de B
que está presente en el __mro__
y que tiene dicho atributo .
Entonces, si comenta la llamada a super en la función init de B , la pila de métodos se detendrá en B.__init__
, y nunca alcanzará C
o A
Para resumir:
- Independientemente de a qué clase se refiera, el
self
siempre es una referencia a la instancia, y sus__mro__
y__class__
permanecen constantes - super () encuentra el método mirando a las clases que están a la derecha del actual en el
__mro__
. Como__mro__
permanece constante, lo que sucede es que se busca como una lista, no como un árbol o un gráfico.
En ese último punto, tenga en cuenta que el nombre completo del algoritmo MRO es la linealización de la clase superpuesta C3 . Es decir, aplana esa estructura en una lista. Cuando ocurren las diferentes llamadas a super()
, están efectuando una iteración efectiva de esa lista.