Pregunta

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

Pensé que lo había entendido hasta que leí este ejemplo (2.6) y me quedé 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 "<stdin>", line 1, in <module>
  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 era lo que esperaba cuando leí esta línea justo antes del ejemplo:

Si estamos usando un método de clase, no tenemos una instancia con la que llamar super. Afortunadamente para nosotros, 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.

¿Fue útil?

Solución

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

En la página vinculada , Ejemplos 2.5 , 2.6 y 2.7 deberían usar un método, do_your_stuff . (Es decir, do_something debe cambiarse a do_your_stuff ).

Además, como Ned Deily señaló , 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 (consulte nota al pie 2 ). Como cls se pasó como segundo argumento a super () , es cls el que se vincula al método devuelto. En otras palabras, cls se pasa como el primer argumento para el método do_your_stuff () de la clase A.

Para reiterar: super (B, cls) .do_your_stuff () hace que el método A do_your_stuff sea llamado con cls pasado como primer argumento. Para que eso funcione, A do_your_stuff tiene que ser un método de clase. La página vinculada no menciona eso, pero ese es definitivamente el caso.

PS. do_something = classmethod (do_something) es la antigua forma de hacer un classmethod. La nueva forma (er) es usar el decorador @classmethod.


Tenga en cuenta que super (B, cls) no se puede reemplazar por super (cls, cls) . Hacerlo podría conducir 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()

elevará RuntimeError: profundidad de recursión máxima excedida al llamar a un objeto Python .

Si cls es C , entonces super (cls, cls) busca C.mro () para 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 super (cls, cls) .do_your_stuff () se llama dentro de B.do_your_stuff , terminas llamando a B.do_your_stuff en un bucle infinito .

En Python3, la forma de 0 argumentos de super se agregó para que super (B, cls) se pueda reemplazar por super () , y Python3 descubrirá por contexto que super ( ) en la definición de clase B debe ser equivalente a super (B, cls) .

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

Otros consejos

En Python 3, puede omitir los argumentos de especificación 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 B.f() == "A's f was called."

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

Su ejemplo usando el método de clase anterior muestra qué es un método de clase: pasa la clase en sí 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
<class '__main__.A'>

El ejemplo de la página web parece funcionar según lo publicado. ¿Creó también un método do_something para la superclase pero no lo convirtió en un método de clase? 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 "<stdin>", line 1, in <module>
  File "<stdin>", 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 encantadora 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 ..

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top