Важно обратить внимание на то, какие значения используются в качестве каждого аргумента для super
.Основная цель super
состоит в том, чтобы выполнить поиск атрибута согласно некоторому порядку разрешения метода (MRO).Второй аргумент определяет , какую MRO использовать;первый определяет, где начать поиск.
MRO всегда определяется классом ;при выполнении разрешения метода для экземпляра мы используем MRO класса, типом которого является этот экземпляр.
В классе
class Meta_1(type):
def __call__(cls, *a, **kw): # line 1
print("entering Meta_1.__call__()")
print(cls) # line 4
print(cls.mro()) # line 5
print(super(Meta_1, cls).__self__) # line 6
rv = super(Meta_1, cls).__call__(*a, **kw) # line 7
print("exiting Meta_1.__call__()")
return rv
мы видим два варианта использования super
.Оба принимают одинаковые аргументы.cls
- это некоторый объект, переданный в качестве первого аргумента Meta_1.__call__
.Это означает, что мы будем использовать MRO, предоставленный type(cls)
, и мы будем использовать первый найденный класс после Meta_1
, который предоставляет требуемый метод.(В первом вызове __self__
является атрибутом самого прокси-объекта, а не атрибутом или методом класса, для которого возвращается прокси super
.)
Когда вы запускаете свой код, вы видите, чтоcls
привязан к вашему объекту типа Car
.Это потому, что Car()
реализован type(Car).__call__()
;поскольку Car
использует Meta_1
в качестве своего метакласса, type(Car)
равен Meta_1
.
cls.mro()
не имеет значения, потому что это MRO, используемое экземплярами из cls
.
MRO самого Meta_1
можно увидеть с помощью
>>> Meta_1.mro(Meta_1)
[<class '__main__.Meta_1'>, <class 'type'>, <class 'object'>]
(mro
- это метод экземпляра класса type
, и поэтому требует, казалось бы, избыточного экземпляра type
в качестве аргумента. Имейте в виду, что cls.mro()
эквивалентно type(cls).mro(cls)
.)
Таким образом, строка 7 является вызовом type.__call__
, чтобы создать экземпляр cls
, который Meta_1.__call__
может вернуться.