Usando super con un método de clase

Estoy tratando de aprender la función super () en Python.

Pensé que lo entendía hasta que llegué a este ejemplo (2.6) y me encontré atrapado.

http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html#super-with-classmethod-example

Traceback (most recent call last): File "", line 1, in  File "test.py", line 9, in do_something do_something = classmethod(do_something) TypeError: unbound method do_something() must be called with B instance as first argument (got nothing instead) >>> 

No fue lo que esperaba cuando leí esta línea justo antes del ejemplo:

Si estamos usando un método de clase, no tenemos una instancia para llamar a super con. Afortunadamente para nosotros, el super funciona incluso con un tipo como segundo argumento. — El tipo se puede pasar directamente a super como se muestra a continuación.

Que es exactamente lo que Python me dice que no es posible diciendo que do_something () debería llamarse con una instancia de B.

A veces, los textos deben leerse más por el sabor de la idea que por los detalles. Este es uno de esos casos.

En la página enlazada , los Ejemplos 2.5, 2.6 y 2.7 deben usar un solo método, do_your_stuff . (Es decir, do_something debe cambiar a do_your_stuff .)

Además, como señaló Ned Deily , A.do_your_stuff tiene que ser un método de clase.

 class A(object): @classmethod def do_your_stuff(cls): print 'This is A' class B(A): @classmethod def do_your_stuff(cls): super(B, cls).do_your_stuff() B.do_your_stuff() 

super(B, cls).do_your_stuff devuelve un método enlazado (ver nota 2 ). Como cls se pasó como el segundo argumento de super() , es cls que se enlaza con el método devuelto. En otras palabras, cls se pasa como el primer argumento del método do_you_stuff() .

Así que super(B, cls).do_your_stuff() hace que se do_your_stuff método do_your_stuff A con cls pasados ​​como primer argumento. Para que eso funcione, Ado_your_stuff tiene que ser un método de clase. La página enlazada no menciona eso, pero es verdad.

PD. do_something = classmethod(do_something) es la forma antigua de hacer un método de clase. La forma nueva (er) es usar el decorador @classmethod.


Tenga en cuenta que super(B, cls) no puede ser reemplazado por super(cls, cls) . Hacerlo podría llevar a bucles infinitos. Por ejemplo,

 class A(object): @classmethod def do_your_stuff(cls): print('This is A') class B(A): @classmethod def do_your_stuff(cls): print('This is B') # super(B, cls).do_your_stuff() # CORRECT super(cls, cls).do_your_stuff() # WRONG class C(B): @classmethod def do_your_stuff(cls): print('This is C') # super(C, cls).do_your_stuff() # CORRECT super(cls, cls).do_your_stuff() # WRONG C.do_your_stuff() 

generará RuntimeError: maximum recursion depth exceeded while calling a Python object .

Si cls es C , entonces super(cls, cls) busca en C.mro() la clase que viene después de C

 In [161]: C.mro() Out[161]: [__main__.C, __main__.B, __main__.A, object] 

Dado que esa clase es B , cuando cls es C , super(cls, cls).do_your_stuff() siempre llama a B.do_your_stuff . Como se llama a super(cls, cls).do_your_stuff() dentro de B.do_your_stuff , terminas llamando a B.do_your_stuff en un bucle infinito.

En Python3, la forma de argumento 0 de super se agregó para que super(B, cls) pueda ser reemplazado por super() , y Python3 descubrirá a partir del contexto que super() en la definición de class B debería ser equivalente a super(B, cls) .

Pero en ninguna circunstancia es super(cls, cls) (o por razones similares, super(type(self), self) ) nunca es correcto.

En Python 3, puede omitir la especificación de argumentos para super ,

 class A: @classmethod def f(cls): return "A's f was called." class B(A): @classmethod def f(cls): return super().f() assert Bf() == "A's f was called." 

He actualizado el artículo para hacerlo más claro: Atributos y métodos de Python # Super

Su ejemplo utilizando el método de clase anterior muestra lo que es un método de clase: pasa la clase en lugar de la instancia como primer parámetro. Pero ni siquiera necesita una instancia para llamar al método, por ejemplo:

 >>> class A(object): ... @classmethod ... def foo(cls): ... print cls ... >>> A.foo() # note this is called directly on the class  

El ejemplo de la página web parece funcionar como publicado. ¿ do_something usted también un método do_something para la superclase pero no lo convirtió en un método class? Algo como esto te dará ese error:

 >>> class A(object): ... def do_something(cls): ... print cls ... # do_something = classmethod(do_something) ... >>> class B(A): ... def do_something(cls): ... super(B, cls).do_something() ... do_something = classmethod(do_something) ... >>> B().do_something() Traceback (most recent call last): File "", line 1, in  File "", line 3, in do_something TypeError: unbound method do_something() must be called with B instance as first argument (got nothing instead) 

Creo que he entendido el punto ahora gracias a este hermoso sitio y hermosa comunidad.

Si no le importa, corríjame si me equivoco en los métodos de clase (que ahora estoy tratando de entender completamente):

 # EXAMPLE #1 >>> class A(object): ... def foo(cls): ... print cls ... foo = classmethod(foo) ... >>> a = A() >>> a.foo() # THIS IS THE CLASS ITSELF (__class__) class '__main__.A' # EXAMPLE #2 # SAME AS ABOVE (With new @decorator) >>> class A(object): ... @classmethod ... def foo(cls): ... print cls ... >>> a = A() >>> a.foo() class '__main__.A' # EXAMPLE #3 >>> class B(object): ... def foo(self): ... print self ... >>> b = B() >>> b.foo() # THIS IS THE INSTANCE WITH ADDRESS (self) __main__.B object at 0xb747a8ec >>> 

Espero que esta ilustración muestre …