¿Por qué un súper argumento de un método de clase necesita un segundo argumento?

Esto funciona como se esperaba:

>>> class Foo(object): ... @classmethod ... def hello(cls): ... print 'hello, foo' ... >>> class Bar(Foo): ... @classmethod ... def hello(cls): ... print 'hello, bar' ... super(Bar, cls).hello() ... >>> b = Bar() >>> b.hello() hello, bar hello, foo 

También puedo llamar explícitamente a la clase base:

 >>> class Bar(Foo): ... @classmethod ... def hello(cls): ... print 'hello, bar' ... Foo.hello() ... >>> b = Bar() >>> b.hello() hello, bar hello, foo 

Me preguntaba por qué no puedo omitir el primer argumento de super , así:

 >>> class Bar(Foo): ... @classmethod ... def hello(cls): ... print 'hello, bar' ... super(Bar).hello() ... >>> b = Bar() >>> b.hello() hello, bar Traceback (most recent call last): File "", line 1, in  File "", line 5, in hello AttributeError: 'super' object has no attribute 'hello' 

cuando el resultado de la super llamada sin un segundo argumento parece ser un tipo de clase dentro de un súper tipo:

 >>> class Bar(Foo): ... @classmethod ... def hello(cls): ... print Foo, type(Foo) ... print super(Bar), type(super(Bar)) ... print cls, type(cls) ... >>> b = Bar() >>> b.hello()   <super: , NULL>    

Supongo que me estoy preguntando sobre el diseño aquí. ¿Por qué necesitaría pasar el objeto de clase a la súper llamada para obtener una referencia al tipo de clase base Foo ? Para un método normal, tiene sentido pasar self a la función, ya que necesita vincular el tipo de clase base a una instancia real de la clase. Pero un método de clase no necesita una instancia específica de la clase.

EDITAR : Me sale el mismo error en Python 3.2 que en 2.7 super(Bar).hello() . Sin embargo, simplemente puedo hacer super().hello() y eso funciona bien.

super() devuelve un descriptor y necesita dos elementos:

  • Un punto de partida desde el que buscar la jerarquía de clases.
  • El argumento para enlazar los métodos devueltos.

Para el caso de los dos argumentos (y el argumento cero implícito * ), el segundo argumento se usa para vincularse, pero si no pasa un segundo argumento, super() no puede invocar el protocolo descriptor para vincular las funciones devueltas, métodos de clase, propiedades u otros descriptores. classmethods son todavía descriptores y están enlazados; el enlace a una clase y no una instancia, pero super() no sabe cómo utilizará el descriptor el contexto al que se une.

super() no debe ni puede saber que está buscando un método de clase en lugar de un método regular; los métodos de clase solo difieren de los métodos regulares porque su método .__get__() actúa de manera diferente.

¿Por qué se unen los métodos de clase? Porque cuando subclasifica Foo pero no anulas .hello() , llamar a Bar.hello() invoca la función Foo.__dict__['hello'] , la vincula a Bar y tu primer argumento a hello(cls) será esa subclase, no Foo

Sin un segundo argumento, super() devuelve un objeto no enlazado que se puede enlazar manualmente más adelante. Puede realizar el enlace usted mismo utilizando el método .__get__() provisto por la instancia super() :

 class Bar(Foo): @classmethod def hello(cls): print 'hello, bar' super(Bar).__get__(cls, None).hello() 

super().__get__() en una instancia sin un contexto devuelve efectivamente una nueva instancia super() con el conjunto de contextos. En una instancia con un contexto .__get__() solo devuelve self ; Ya está atado.


* En Python 3, llamar a super() sin argumentos dentro de un método enlazado usará el marco de llamada para descubrir, implícitamente, qué tipo y objeto enlazado son, por lo que ya no tendrá que pasar explícitamente los argumentos de tipo y objeto. caso. Python 3 en realidad agrega una variable de cierre __class__ implícita a los métodos para este propósito. Ver PEP 3135 y ¿Por qué es super () la magia de Python 3.x?