При дзвінку 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)