Bei einem Anruf super()
um eine Klassenmethode, Instanzmethode oder statische Methode in die Version eines Elternteils aufzulösen, müssen wir die aktuelle Klasse, in deren Bereich wir uns befinden, als erstes Argument übergeben, um anzugeben, in welchen Bereich des Elternteils wir aufzulösen versuchen, und als zweites Argument das Objekt von Interesse, um anzugeben, auf welches Objekt wir versuchen, diesen Bereich anzuwenden.
Betrachten Sie eine Klassenhierarchie A
, B
y C
wobei jede Klasse die übergeordnete Klasse der nachfolgenden Klasse ist, und a
, b
y c
die jeweiligen Instanzen von jedem.
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
Verwendung von super
mit einer statischen Methode
z.B. mit super()
aus dem __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)
Erläuterung:
1- auch wenn es üblich ist, dass __new__()
als ersten Parameter einen Verweis auf die aufrufende Klasse zu nehmen, ist es no in Python als classmethod implementiert, sondern als staticmethod. Das heißt, ein Verweis auf eine Klasse muss explizit als erstes Argument übergeben werden, wenn man __new__()
direkt:
# 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- beim Aufruf super()
um zur übergeordneten Klasse zu gelangen, übergeben wir die untergeordnete Klasse A
als erstes Argument, dann übergeben wir einen Verweis auf das Objekt von Interesse, in diesem Fall ist es der Klassenverweis, der übergeben wurde, als A.__new__(cls)
genannt wurde. In den meisten Fällen handelt es sich dabei auch um einen Verweis auf die Kindklasse. In manchen Situationen ist dies nicht der Fall, z. B. bei der Vererbung über mehrere Generationen.
super(A, cls)
3- da in der Regel __new__()
ist eine statische Methode, super(A, cls).__new__
gibt ebenfalls eine statische Methode zurück und muss explizit mit allen Argumenten versehen werden, einschließlich des Verweises auf das betreffende Objekt, in diesem Fall cls
.
super(A, cls).__new__(cls, *a, **kw)
4- das Gleiche tun, ohne super
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return object.__new__(cls, *a, **kw)
Verwendung von super
mit einer Instanzmethode
z.B. mit super()
von innen __init__()
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
super(A, self).__init__(*a, **kw)
Erläuterung:
1- __init__
ist eine Instanzmethode, was bedeutet, dass sie als erstes Argument einen Verweis auf eine Instanz annimmt. Wenn sie direkt von der Instanz aufgerufen wird, wird der Verweis implizit übergeben, d.h. Sie müssen ihn nicht angeben:
# 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- beim Aufruf super()
innerhalb __init__()
übergeben wir die Kindklasse als erstes Argument und das gewünschte Objekt als zweites Argument, das im Allgemeinen ein Verweis auf eine Instanz der Kindklasse ist.
super(A, self)
3- Der Anruf super(A, self)
gibt einen Proxy zurück, der den Bereich auflöst und ihn auf self
als ob es jetzt eine Instanz der übergeordneten Klasse wäre. Nennen wir diesen Proxy s
. Seit __init__()
eine Instanzmethode ist, wird der Aufruf s.__init__(...)
wird implizit eine Referenz von self
als erstes Argument für die übergeordnete __init__()
.
4- dasselbe zu tun, ohne super
müssen wir einen Verweis auf eine Instanz explizit an die übergeordnete Version von __init__()
.
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
object.__init__(self, *a, **kw)
Verwendung von super
mit einer Klassenmethode
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)
Erläuterung:
1- Eine Klassenmethode kann direkt von der Klasse aus aufgerufen werden und erhält als ersten Parameter einen Verweis auf die Klasse.
# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()
2- beim Aufruf super()
innerhalb einer Klassenmethode auf die Version des Elternteils aufzulösen, wollen wir die aktuelle Kindklasse als erstes Argument übergeben, um anzugeben, auf welchen Bereich des Elternteils wir auflösen wollen, und das Objekt von Interesse als zweites Argument, um anzugeben, auf welches Objekt wir diesen Bereich anwenden wollen, was im Allgemeinen eine Referenz auf die Kindklasse selbst oder eine ihrer Unterklassen ist.
super(B, cls_or_subcls)
3- Der Anruf super(B, cls)
löst sich auf in den Geltungsbereich von A
und wendet sie an auf cls
. Seit alternate_constructor()
eine Klassenmethode ist, wird der Aufruf super(B, cls).alternate_constructor(...)
wird implizit eine Referenz von cls
als erstes Argument für A
Die Version von alternate_constructor()
super(B, cls).alternate_constructor()
4-, um dasselbe zu tun, ohne super()
müssen Sie einen Verweis auf die Ungebunden Version von A.alternate_constructor()
(d.h. die explizite Version der Funktion). Dies einfach zu tun, würde nicht funktionieren:
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)
Dies würde nicht funktionieren, da die A.alternate_constructor()
Methode nimmt einen impliziten Verweis auf A
als sein erstes Argument. Die Website cls
das hier übergeben wird, wäre das zweite 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)