При дзвінку super()
вирішити батьківську версію класового методу, методу екземпляра або staticmethod, ми хочемо передати поточний клас, область якого ми знаходимося в якості першого аргументу, щоб вказати, до якої області батьків ми намагаємось вирішити, і як другий аргумент, що цікавить об'єкт, щоб вказати, до якого об'єкта ми намагаємось застосувати цю область.
Розглянемо ієрархію класів 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- Метод class може бути викликаний з класу безпосередньо і приймає за свій перший параметр посилання на клас.
# 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()
в межах classmethod для вирішення його версії для батьків, ми хочемо передати поточний дочірній клас як перший аргумент, щоб вказати, до якої області батьків ми намагаємось вирішити, та об'єкт, що цікавить, як другий аргумент щоб вказати, до якого об'єкта ми хочемо застосувати це поле, яке взагалі є посиланням на сам дочірній клас або на один з його підкласів.
super(B, cls_or_subcls)
3- Виклик super(B, cls)
вирішується в межах сфери застосування A
та застосовується до нього cls
. Оскільки alternate_constructor()
є методом class, виклик super(B, cls).alternate_constructor(...)
неявно передасть посилання cls
як перший аргумент на A
версію 'salternate_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)