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, By Cdonde cada clase es el padre de la que le sigue, y a, by cinstancias 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 supercon 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 Acomo 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 supercon 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á selfcomo 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 selfcomo el primer argumento al padre__init__() .
4- hacer lo mismo sin que supertengamos 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 supercon 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 Ay 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 clscomo primer argumento a Ala 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 Acomo su primer argumento. El clsser 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)