Usando super con un método de clase

6 minutos de lectura

avatar de usuario
dza

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

Pensé que lo había entendido hasta que llegué a este ejemplo (2.6) y me encontré atascado.

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 para llamar a 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 al decir que se debe llamar a do_something() con una instancia de B.

  • Posible duplicado de llamar al método de clase de una clase base en Python

    – Sr_y_Sra_D

    3 de noviembre de 2017 a las 12:09

avatar de usuario
unutbu

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 el pagina enlazadaLos ejemplos 2.5, 2.6 y 2.7 deben usar un solo método, do_your_stuff. (Eso es, do_something debe ser cambiado 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 vinculado método (ver nota al pie 2). Ya que cls fue pasado como el segundo argumento para super()es cls que se une al método devuelto. En otras palabras, cls se pasa como el primer argumento del método do_your_stuff() de clase A.

Reiterar: super(B, cls).do_your_stuff() causas A‘s do_your_stuff método para ser llamado con cls pasó como el primer argumento. Para que eso funcione, A‘s
do_your_stuff tiene que ser un método de clase. La página vinculada no menciona eso, pero ese es definitivamente el caso.

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


Tenga en cuenta que super(B, cls) no puede ser reemplazado por super(cls, cls). Si lo hace, podría dar lugar 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: maximum recursion depth exceeded while calling a Python object.

Si cls es Cdespués super(cls, cls) búsquedas C.mro() para la clase que viene después C.

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

Como esa clase es Bcuando cls es C, super(cls, cls).do_your_stuff() siempre llamadas B.do_your_stuff. Ya que super(cls, cls).do_your_stuff() se llama adentro B.do_your_stuffterminas llamando B.do_your_stuff en un bucle infinito.

En Python3, el Forma de 0 argumentos de super se agregó así super(B, cls) podría ser reemplazado por super()y Python3 se dará cuenta del contexto que super() en la definición de class 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.

  • ¿Existen dificultades al usar super(cls, cls)?

    – George Moutsopoulos

    30 de agosto de 2018 a las 17:04


  • @GeorgeMoutsopoulos: super(cls, cls) es casi seguro un error. He editado la publicación anterior para explicar por qué.

    – unutbu

    30 de agosto de 2018 a las 17:30

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 B.f() == "A's f was called."

  • Esta respuesta podría trasladarse a otra pregunta si esta pregunta en particular se trata específicamente de Python 2 (es difícil saberlo ya que el sitio web vinculado, CafePy, ya no está disponible).

    – Tankobot

    12 de noviembre de 2017 a las 9:00

  • No me funciona en un proyecto mío. esta dando RuntimeError: super(): no arguments

    – Madtyn

    5 de febrero de 2019 a las 16:25

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

Su ejemplo que usa 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 tal como se publicó. ¿Creaste un do_something método para la superclase también pero no convertirlo 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 ahora entendí el punto gracias a este hermoso sitio y a esta 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..

  • Para obtener una explicación de los métodos de clase, tal vez encuentre esto útil: stackoverflow.com/questions/1669445/…

    – unutbu

    30 de noviembre de 2009 a las 17:19

  • Para obtener una explicación de los métodos de clase, tal vez encuentre esto útil: stackoverflow.com/questions/1669445/…

    – unutbu

    30 de noviembre de 2009 a las 17:19

¿Ha sido útil esta solución?