Lorsque vous appelez super()
pour résoudre la version d'un parent d'une méthode de classe, d'une méthode d'instance ou d'une méthode statique, nous voulons passer la classe actuelle dont la portée est dans le premier argument, pour indiquer la portée du parent que nous essayons de résoudre et en tant que un deuxième argument l'objet d'intérêt pour indiquer à quel objet nous essayons d'appliquer cette portée.
Considérons une hiérarchie de classes A
, B
et C
où chaque classe est le parent de celui qui le suit, et a
, b
et c
instances de chaque respectives.
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
Utilisation super
avec une méthode statique
par exemple en utilisant super()
de l'intérieur de la __new__()
méthode
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return super(A, cls).__new__(cls, *a, **kw)
Explication:
1- même s'il est habituel __new__()
de prendre comme premier paramètre une référence à la classe appelante, il n'est pas implémenté en Python comme méthode de classe, mais plutôt comme méthode statique. Autrement dit, une référence à une classe doit être passée explicitement comme premier argument lors de l'appel __new__()
direct:
# 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- lors de l'appel super()
pour accéder à la classe parent, nous passons la classe enfant A
comme premier argument, puis nous passons une référence à l'objet d'intérêt, dans ce cas, c'est la référence de classe qui a été transmise lors de l' A.__new__(cls)
appel. Dans la plupart des cas, il s'agit également d'une référence à la classe enfant. Dans certaines situations, cela peut ne pas être le cas, par exemple dans le cas d'héritages de plusieurs générations.
super(A, cls)
3- puisque comme une règle générale __new__()
est une méthode statique, super(A, cls).__new__
retourne également une méthode statique et doit être fourni explicitement tous les arguments, y compris la référence à l'objet de insterest, dans ce cas cls
.
super(A, cls).__new__(cls, *a, **kw)
4- Faire la même chose sans super
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return object.__new__(cls, *a, **kw)
Utilisation super
avec une méthode d'instance
par exemple en utilisant super()
de l'intérieur__init__()
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
super(A, self).__init__(*a, **kw)
Explication:
1- __init__
est une méthode d'instance, ce qui signifie qu'elle prend comme premier argument une référence à une instance. Lorsqu'elle est appelée directement à partir de l'instance, la référence est transmise implicitement, c'est-à-dire que vous n'avez pas besoin de la spécifier:
# 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- lors de l'appel à l' super()
intérieur, __init__()
nous passons la classe enfant comme premier argument et l'objet d'intérêt comme deuxième argument, qui en général est une référence à une instance de la classe enfant.
super(A, self)
3- L'appel super(A, self)
renvoie un proxy qui résoudra la portée et l'appliquera self
comme s'il s'agissait désormais d'une instance de la classe parente. Appelons ce proxy s
. Puisque __init__()
c'est une méthode d'instance, l'appel s.__init__(...)
passera implicitement une référence de self
comme premier argument à celle du parent __init__()
.
4- pour faire de même sans super
avoir besoin de passer explicitement une référence à une instance à la version parent de __init__()
.
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
object.__init__(self, *a, **kw)
Utilisation super
avec une méthode de classe
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)
Explication:
1- Une méthode de classe peut être appelée directement depuis la classe et prend comme premier paramètre une référence à la classe.
# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()
2- Lorsque nous appelons super()
dans une méthode de classe pour résoudre la version de son parent, nous voulons passer la classe enfant actuelle comme premier argument pour indiquer la portée du parent que nous essayons de résoudre et l'objet d'intérêt comme deuxième argument pour indiquer à quel objet nous voulons appliquer cette portée, qui est en général une référence à la classe enfant elle-même ou à l'une de ses sous-classes.
super(B, cls_or_subcls)
3- L'appel super(B, cls)
résout le champ d'application A
et l'applique cls
. Puisque alternate_constructor()
c'est une méthode de classe, l'appel super(B, cls).alternate_constructor(...)
passera implicitement une référence de cls
comme premier argument à A
la version dealternate_constructor()
super(B, cls).alternate_constructor()
4- Pour faire de même sans utiliser, super()
vous devez obtenir une référence à la version non liée de A.alternate_constructor()
(c'est-à-dire la version explicite de la fonction). Faire simplement cela ne fonctionnerait pas:
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)
Ce qui précède ne fonctionnerait pas car la A.alternate_constructor()
méthode prend une référence implicite à A
comme premier argument. L' cls
être passé ici serait son deuxième argument.
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)