Ответ 1
Функциональные объекты Python являются дескрипторами, а Python использует протокол дескриптора для привязки функций к экземпляру. Этот процесс производит связанный метод.
Привязка - это то, что заставляет "магический" self
аргумент появляться, когда вы вызываете метод, и то, что заставляет объект property
автоматически вызывать методы, когда вы пытаетесь использовать свойство как атрибут в экземплярах.
super()
с двумя аргументами вызывает тот же протокол дескриптора, когда вы пытаетесь использовать его для поиска методов в родительских классах; super(Foo, self).bar()
будет проходить родительские классы Foo
до тех пор, пока не будет найдена bar
атрибута, и если это объект, являющийся дескриптором, он будет привязан к self
. Вызов bar
затем вызывает связанный метод, который в свою очередь вызывает функцию, передающую аргумент self
как bar(self)
.
Для этого объект super()
хранит класс (первый аргумент) и self
(второй аргумент) для привязки, а также тип аргумента self
в качестве атрибутов __thisclass__
, __self__
и __self_class__
соответственно:
>>> class Foo:
... def bar(self):
... return 'bar on Foo'
...
>>> class Spam(Foo):
... def bar(self):
... return 'bar on Spam'
...
>>> spam = Spam()
>>> super(Spam, spam)
<super: <class 'Spam'>, <Spam object>>
>>> super(Spam, spam).__thisclass__
<class '__main__.Spam'>
>>> super(Spam, spam).__self__
<__main__.Spam object at 0x107195c10>
>>> super(Spam, spam).__self_class__
<class '__main__.Spam'>
При поиске атрибутов выполняется __mro__
атрибута __mro__
атрибута __self_class__
, начиная с одной позиции после позиции __thisclass__
, и результаты связываются.
__self_class__
атрибута super()
с одним аргументом атрибутам __self__
и __self_class__
присвоено значение None
и он пока не может выполнять поиск:
>>> super(Spam)
<super: <class 'Spam'>, NULL>
>>> super(Spam).__self__ is None
True
>>> super(Spam).__self_class__ is None
True
>>> super(Spam).bar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'super' object has no attribute 'bar'
Объект поддерживает протокол дескриптора, поэтому вы можете связать его так же, как вы можете связать метод:
>>> super(Spam).__get__(spam, Spam)
<super: <class 'Spam'>, <Spam object>>
>>> super(Spam).__get__(spam, Spam).bar()
'bar on Foo'
Это означает, что вы можете хранить такой объект в классе и использовать его для перехода к родительским методам:
>>> class Eggs(Spam):
... pass
...
>>> Eggs.parent = super(Eggs)
>>> eggs = Eggs()
>>> eggs.parent
<super: <class 'Eggs'>, <Eggs object>>
>>> eggs.parent.bar()
'bar on Spam'
Основным вариантом использования было бы избежать поиска класса каждый раз, когда вы используете super()
:
class Foo:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
setattr(cls, f"_{cls.__name__}__sup", super(cls))
def bar(self):
return 'bar on Foo'
class Spam(Foo):
def bar(self):
return "spammed: " + self.__sup.bar()
но это ломается при использовании метода класса (так как cls.__sup
wont bind) и был заменен Python 3s super()
с нулевыми аргументами.