Как я могу использовать super() с одним аргументом в python

Прочитав о super() object в Python, я прочитал следующее утверждение:

Если второй аргумент опущен, вернувшийся объект возвращается unbound

Что это значит, и как я могу использовать super() с одним аргументом в коде?

Ответы

Ответ 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() с нулевыми аргументами.