Al llamar super()
para resolver la versión de un método de clase, método de instancia o método estático de un padre, queremos pasar la clase actual en cuyo alcance estamos como primer argumento, para indicar a qué alcance del padre estamos tratando de resolver, y como un segundo argumento el objeto de interés para indicar a qué objeto estamos tratando de aplicar ese alcance
Considere una jerarquía de clases A
, B
y C
donde cada clase es el padre de la que le sigue, y a
, b
y c
instancias respectivas de cada uno.
super(B, b)
# resolves to the scope of B's parent i.e. A
# and applies that scope to b, as if b was an instance of A
super(C, c)
# resolves to the scope of C's parent i.e. B
# and applies that scope to c
super(B, c)
# resolves to the scope of B's parent i.e. A
# and applies that scope to c
Usar super
con un método estático
por ejemplo, usar super()
desde dentro del __new__()
método
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return super(A, cls).__new__(cls, *a, **kw)
Explicación:
1- pesar de que es habitual para __new__()
tomar como primer parámetro una referencia a la clase de llamada, se no implementado en Python como classmethod, sino más bien un métodoestático. Es decir, una referencia a una clase debe pasarse explícitamente como primer argumento cuando se llama __new__()
directamente:
# if you defined this
class A(object):
def __new__(cls):
pass
# calling this would raise a TypeError due to the missing argument
A.__new__()
# whereas this would be fine
A.__new__(A)
2- cuando super()
llamamos para llegar a la clase principal, pasamos la clase secundaria A
como primer argumento, luego pasamos una referencia al objeto de interés, en este caso, es la referencia de clase que se pasó cuando A.__new__(cls)
se llamó. En la mayoría de los casos, también es una referencia a la clase secundaria. En algunas situaciones, podría no serlo, por ejemplo, en el caso de herencias de múltiples generaciones.
super(A, cls)
3- dado que, como regla general, __new__()
es un método estático, super(A, cls).__new__
también devolverá un método estático y debe proporcionarse todos los argumentos explícitamente, incluida la referencia al objeto de interés, en este caso cls
.
super(A, cls).__new__(cls, *a, **kw)
4- haciendo lo mismo sin super
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return object.__new__(cls, *a, **kw)
Usar super
con un método de instancia
por ejemplo, usar super()
desde dentro__init__()
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
super(A, self).__init__(*a, **kw)
Explicación:
1- __init__
es un método de instancia, lo que significa que toma como primer argumento una referencia a una instancia. Cuando se llama directamente desde la instancia, la referencia se pasa implícitamente, es decir, no necesita especificarla:
# you try calling `__init__()` from the class without specifying an instance
# and a TypeError is raised due to the expected but missing reference
A.__init__() # TypeError ...
# you create an instance
a = A()
# you call `__init__()` from that instance and it works
a.__init__()
# you can also call `__init__()` with the class and explicitly pass the instance
A.__init__(a)
2- al llamar super()
dentro __init__()
, pasamos la clase secundaria como primer argumento y el objeto de interés como segundo argumento, que en general es una referencia a una instancia de la clase secundaria.
super(A, self)
3- La llamada super(A, self)
devuelve un proxy que resolverá el alcance y lo aplicará self
como si ahora fuera una instancia de la clase principal. Llamemos a ese proxy s
. Dado que __init__()
es un método de instancia, la llamada s.__init__(...)
pasará implícitamente una referencia de self
como el primer argumento al padre__init__()
.
4- hacer lo mismo sin que super
tengamos que pasar una referencia a una instancia explícitamente a la versión principal de __init__()
.
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
object.__init__(self, *a, **kw)
Usar super
con un método de clase
class A(object):
@classmethod
def alternate_constructor(cls, *a, **kw):
print "A.alternate_constructor called"
return cls(*a, **kw)
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
return super(B, cls).alternate_constructor(*a, **kw)
Explicación:
1- Se puede llamar a un método de clase directamente desde la clase y toma como primer parámetro una referencia a la clase.
# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()
2- cuando se llama super()
dentro de un método de clase para resolver su versión principal, queremos pasar la clase secundaria actual como primer argumento para indicar a qué alcance primario estamos tratando de resolver, y el objeto de interés como segundo argumento para indicar a qué objeto queremos aplicar ese alcance, que en general es una referencia a la clase secundaria en sí o a una de sus subclases.
super(B, cls_or_subcls)
3- La llamada se super(B, cls)
resuelve al alcance A
y se aplica a ella cls
. Como alternate_constructor()
es un método de clase, la llamada super(B, cls).alternate_constructor(...)
pasará implícitamente una referencia de cls
como primer argumento a A
la versión dealternate_constructor()
super(B, cls).alternate_constructor()
4- para hacer lo mismo sin usar super()
, necesitaría obtener una referencia a la versión independiente de A.alternate_constructor()
(es decir, la versión explícita de la función). Simplemente hacer esto no funcionaría:
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
return A.alternate_constructor(cls, *a, **kw)
Lo anterior no funcionaría porque el A.alternate_constructor()
método toma una referencia implícita A
como su primer argumento. El cls
ser pasado aquí sería su segundo argumento.
class B(A):
@classmethod
def alternate_constructor(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
print "B.alternate_constructor called"
# first we get a reference to the unbound
# `A.alternate_constructor` function
unbound_func = A.alternate_constructor.im_func
# now we call it and pass our own `cls` as its first argument
return unbound_func(cls, *a, **kw)