Podczas wywoływania super()
rozwiązania z wersją nadrzędną metody klasy, metody instancji lub metody statycznej chcemy przekazać bieżącą klasę, w której zakresie jesteśmy pierwszym argumentem, aby wskazać zakres nadrzędny, który próbujemy rozwiązać, i jako drugi argument będący przedmiotem zainteresowania wskazujący, do którego obiektu próbujemy zastosować ten zakres.
Rozważmy hierarchii klasa A
, B
i C
gdzie każda klasa jest rodzicem jednego po nim, i a
, b
i c
odpowiednie instancje każdy.
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
Korzystanie super
z metody statycznej
np. korzystanie super()
z __new__()
metody
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return super(A, cls).__new__(cls, *a, **kw)
Wyjaśnienie:
1 - chociaż zwykle __new__()
przyjmuje się jako pierwszy param odniesienie do klasy wywołującej, nie jest on zaimplementowany w Pythonie jako metoda klasy, ale raczej metoda statyczna. Oznacza to, że odwołanie do klasy musi zostać jawnie przekazane jako pierwszy argument podczas __new__()
bezpośredniego wywoływania :
# 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- podczas wywoływania, super()
aby dostać się do klasy nadrzędnej, przekazujemy klasę potomną A
jako pierwszy argument, a następnie przekazujemy odniesienie do obiektu będącego przedmiotem zainteresowania, w tym przypadku jest to odwołanie do klasy, które zostało przekazane, gdy A.__new__(cls)
zostało wywołane. W większości przypadków jest to również odniesienie do klasy podrzędnej. W niektórych sytuacjach może tak nie być, na przykład w przypadku dziedziczenia wielu generacji.
super(A, cls)
3 - ponieważ ogólną zasadą __new__()
jest metoda statyczna, super(A, cls).__new__
zwróci również metodę statyczną i w tym przypadku należy podać jawnie wszystkie argumenty, w tym odniesienie do obiektu zainteresowania cls
.
super(A, cls).__new__(cls, *a, **kw)
4- robienie tego samego bez super
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return object.__new__(cls, *a, **kw)
Korzystanie super
z metody instancji
np. korzystanie super()
z wewnątrz__init__()
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
super(A, self).__init__(*a, **kw)
Wyjaśnienie:
1- __init__
to metoda instancji, co oznacza, że jako pierwszy argument przyjmuje odwołanie do instancji. Po wywołaniu bezpośrednio z instancji odwołanie jest przekazywane niejawnie, co oznacza, że nie trzeba go określać:
# 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- podczas wywoływania super()
wewnątrz __init__()
przekazujemy klasę potomną jako pierwszy argument, a przedmiot zainteresowania jako drugi argument, który ogólnie jest odniesieniem do instancji klasy potomnej.
super(A, self)
3- Wywołanie super(A, self)
zwraca serwer proxy, który rozwiąże zakres i zastosuje go self
tak, jakby był teraz instancją klasy nadrzędnej. Nazwijmy to proxy s
. Ponieważ __init__()
jest to metoda instancji, wywołanie s.__init__(...)
domyślnie przekaże referencję self
jako pierwszy argument do rodzica __init__()
.
4- aby zrobić to samo bez super
musimy przekazać odwołanie do instancji jawnie do wersji rodzica __init__()
.
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
object.__init__(self, *a, **kw)
Używanie super
metody klasowej
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)
Wyjaśnienie:
1- Metodę klasy można wywołać bezpośrednio z klasy i przyjmuje jako swój pierwszy parametr odwołanie do klasy.
# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()
2- podczas wywoływania super()
w ramach metody klasy w celu rozstrzygnięcia jej wersji nadrzędnej, chcemy przekazać bieżącą klasę podrzędną jako pierwszy argument wskazujący zakres nadrzędny, którego próbujemy rozstrzygnąć, oraz obiekt będący przedmiotem zainteresowania jako drugi argument do wskazania, do którego obiektu chcemy zastosować ten zakres, co ogólnie jest odniesieniem do samej klasy potomnej lub jednej z jej podklas.
super(B, cls_or_subcls)
3- Połączenie super(B, cls)
jest rozpatrywane w zakresie A
i dotyczy go cls
. Ponieważ alternate_constructor()
jest to metoda klasy, wywołanie super(B, cls).alternate_constructor(...)
domyślnie przekaże referencję cls
jako pierwszy argument do A
wersjialternate_constructor()
super(B, cls).alternate_constructor()
4- aby zrobić to samo bez użycia super()
, musisz uzyskać odniesienie do niezwiązanej wersji A.alternate_constructor()
(tj. Jawnej wersji funkcji). Po prostu zrobienie tego nie zadziałałoby:
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)
Powyższe nie zadziałałoby, ponieważ A.alternate_constructor()
metoda przyjmuje A
jako pierwsze odwołanie niejawne odniesienie . cls
Przepuszcza tutaj będzie jego drugi 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)