При вызове 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()
get для родительского класса мы передаем дочерний класс в 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)