При вызове super()
для разрешения родительской версии метода класса, метода экземпляра или статического метода мы хотим передать текущий класс, область действия которого мы находимся в качестве первого аргумента, чтобы указать область родительского элемента, которую мы пытаемся разрешить и в качестве второго аргумента интересующий объект, чтобы указать, к какому объекту мы пытаемся применить эту область.
Рассмотрим иерархию классов A
, B
и C
, где каждый класс является родительским для следующего за ним, и a
, b
и c
соответствующих экземпляров каждого.
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
Использование super
со статическим методом
например. используя super()
из __new__()
метода
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return super(A, cls).__new__(cls, *a, **kw)
Объяснение:
1 - даже если __new__()
обычно принимает в качестве первого параметра ссылку на вызывающий класс, не реализован в Python как метод класса, а скорее как статический метод. То есть ссылка на класс должна быть явно передана в качестве первого аргумента при непосредственном вызове __new__()
:
# 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 - при вызове super()
для перехода к родительскому классу мы передаем дочерний класс A
в качестве первого аргумента, затем передаем ссылку на интересующий объект, в данном случае это ссылка на класс, которая была передана когда A.__new__(cls)
был вызван. В большинстве случаев это также является ссылкой на дочерний класс. В некоторых ситуациях это может быть не так, например, в случае наследования нескольких поколений.
super(A, cls)
3 - поскольку, как правило, __new__()
является статическим методом, super(A, cls).__new__
также будет возвращать статический метод, и ему необходимо явно указать все аргументы, включая ссылку на объект интереса, в данном случае cls
.
super(A, cls).__new__(cls, *a, **kw)
4 - делать то же самое без super
class A(object):
def __new__(cls, *a, **kw):
# ...
# whatever you want to specialize or override here
# ...
return object.__new__(cls, *a, **kw)
Использование super
с методом экземпляра
например. используя super()
изнутри __init__()
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
super(A, self).__init__(*a, **kw)
Пояснение:
1- __init__
- это метод экземпляра, означающий, что в качестве первого аргумента он принимает ссылку на экземпляр. При вызове непосредственно из экземпляра ссылка передается неявно, то есть вам не нужно указывать ее:
# 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- при вызове super()
в __init__()
мы передаем дочерний класс в качестве первого аргумента, а интересующий объект - в качестве второго аргумента, который в общем случае является ссылкой на экземпляр дочернего класса.
super(A, self)
3. Вызов super(A, self)
возвращает прокси-сервер, который разрешит область и применит ее к self
, как если бы он стал экземпляром родительского класса. Давайте назовем этот прокси s
. Поскольку __init__()
является методом экземпляра, вызов s.__init__(...)
неявно передаст ссылку self
в качестве первого аргумента родительскому __init__()
.
4 - чтобы сделать то же самое без super
, нам нужно явно передать ссылку на экземпляр в версию родительского элемента __init__()
.
class A(object):
def __init__(self, *a, **kw):
# ...
# you make some changes here
# ...
object.__init__(self, *a, **kw)
Использование super
с классом
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)
Пояснение:
1- Метод класса может быть вызван напрямую из класса и принимает в качестве первого параметра ссылку на класс.
# calling directly from the class is fine,
# a reference to the class is passed implicitly
a = A.alternate_constructor()
b = B.alternate_constructor()
2 - при вызове super()
в методе класса для разрешения его родительской версии мы хотим передать текущий дочерний класс в качестве первого аргумента, указывающего, в какую область видимости родителя мы пытаемся разрешить, и объект представляет интерес как второй аргумент, указывающий, к какому объекту мы хотим применить эту область, которая в общем случае является ссылкой на сам дочерний класс или один из его подклассов.
super(B, cls_or_subcls)
3- Вызов super(B, cls)
разрешается до объема A
и применяется к cls
. Поскольку alternate_constructor()
является методом класса, вызов super(B, cls).alternate_constructor(...)
неявно передаст ссылку cls
в качестве первого аргумента для версии A
alternate_constructor()
super(B, cls).alternate_constructor()
4 - чтобы сделать то же самое без использования super()
, вам потребуется получить ссылку на несвязанную версию A.alternate_constructor()
(то есть явную версию функции). Простое выполнение этого не сработает:
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)
Выше не будет работать, потому что метод A.alternate_constructor()
принимает неявную ссылку на A
в качестве первого аргумента. Передаваемый здесь cls
будет вторым аргументом.
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)