Говоря оsuper, все могут подумать, что это очень просто — разве он не просто используется для вызова методов родительского класса? Если бы всё было так просто, этой статьи не существовало бы. Давайте разберём это подробно.

Соглашения

Прежде чем начать, давайте договоримся об используемой в этой статье версии Python. По умолчанию мы используем Python 3, а это значит, что все классы, определённые в этой статье, являются классами нового стиля. Если вы используете Python 2, не забудьте наследоваться отobject:
# 默认, Python 3
class A:
    pass

# Python 2
class A(object):
    pass

Ещё одно различие между Python 3 и Python 2 состоит в том, что в Python 3 можно напрямую использоватьsuper().xxxвместоsuper(Class, self).xxx :

# 默认,Python 3
class B(A):
    def add(self, x):
        super().add(x)

# Python 2
class B(A):
    def add(self, x):
        super(B, self).add(x)

Итак, если вы используете Python 2, не забудьте заменить в статьеsuper()наsuepr(Class, self) 。

Если есть другие случаи, несовместимые с Python 2, я отмечу их в статье.

Одиночное наследование

При одиночном наследовании,superкак все и думают, super в основном используется для вызова методов родительского класса.

class A:
    def __init__(self):
        self.n = 2

    def add(self, m):
        print('self is {0} @A.add'.format(self))
        self.n += m


class B(A):
    def __init__(self):
        self.n = 3

    def add(self, m):
        print('self is {0} @B.add'.format(self))
        super().add(m)
        self.n += 3

Как вы думаете, чему будет равно значение b.nпосле выполнения следующего кода?

b = B()
b.add(2)
print(b.n)

Результат выполнения выглядит следующим образом:

self is <__main__.B object at 0x106c49b38> @B.add
self is <__main__.B object at 0x106c49b38> @A.add
8

Этот результат иллюстрирует два момента:

  • 1, super().add(m)Он действительно вызывает методaddродительского класса A.
  • 2, super().add(m)При вызове метода родительского классаdef add(self, m), в этот момент в родительском классеselfявляется не экземпляром родительского класса, а экземпляром дочернего класса, поэтомуb.add(2)результат после этого —5, а не4 。

Интересно, совпадает ли этот результат с тем, что вы ожидали? Далее давайте рассмотрим пример множественного наследования.

Множественное наследование

На этот раз давайте определим ещё одинclass C, иclass D:

class C(A):
    def __init__(self):
        self.n = 4

    def add(self, m):
        print('self is {0} @C.add'.format(self))
        super().add(m)
        self.n += 4


class D(B, C):
    def __init__(self):
        self.n = 5

    def add(self, m):
        print('self is {0} @D.add'.format(self))
        super().add(m)
        self.n += 5

Что выведет следующий код?

d = D()
d.add(2)
print(d.n)

На этот раз вывод такой:

self is <__main__.D object at 0x10ce10e48> @D.add
self is <__main__.D object at 0x10ce10e48> @B.add
self is <__main__.D object at 0x10ce10e48> @C.add
self is <__main__.D object at 0x10ce10e48> @A.add
19

Вы угадали? Возможно, вы думаете, что вывод приведённого выше кода будет похож на:

self is <__main__.D object at 0x10ce10e48> @D.add
self is <__main__.D object at 0x10ce10e48> @B.add
self is <__main__.D object at 0x10ce10e48> @A.add
15

Почему это отличается от того, что ожидалось? Давайте вместе посмотрим наsuperзагадку.

super — это класс

Когда мы вызываемsuper(), мы на самом деле создаём экземплярsuper-класса. Вы правильно прочитали,super— это класс, а не ключевое слово, не функция и не другая структура данных:

>>> class A: pass
...
>>> s = super(A)
>>> type(s)
<class 'super'>
>>>

В большинстве случаевsuperсодержит две очень важные части информации: MRO и класс в этом MRO. При вызове следующим образомsuper:

super(a_type, obj)

MROотносится к MRO, относящемуся кtype(obj), а класс в MRO — этоa_type, и в то же времяisinstance(obj, a_type) == True 。

При таком вызове:

super(type1, type2)

MRO относится кtype2изMRO, MRO, класс в котором — этоtype1, и в то же времяissubclass(type2, type1) == True 。

Итак,super()что же он на самом деле делает? Проще говоря: предоставитьMROиMROкласс вC , super()вернёт объект, который ищет методы изMROсредиCклассов после него.

То есть поиск не похож на обычный метод, который перебирает всеMROклассы, а скорее ищет вMROхвост.

Например, существуетMRO:

[A, B, C, D, E, object]

следующий вызов:

super(C, A).foo()

superбудет искать только начиная сC, то есть: толькоDилиEилиobjectбудет произведён поискfooметода.

Как работает super при множественном наследовании

Давайте вернёмся к более раннему

d = D()
d.add(2)
print(d.n)

Теперь у вас, возможно, есть некоторое представление, почему вывод —

self is <__main__.D object at 0x10ce10e48> @D.add
self is <__main__.D object at 0x10ce10e48> @B.add
self is <__main__.D object at 0x10ce10e48> @C.add
self is <__main__.D object at 0x10ce10e48> @A.add
19

, верно? ;)

Давайте подробно разберём это ниже:

  • DMRO дляMRO— это:[D, B, C, A, object] 。 Примечание:Вы можете просмотретьD.mro()(в Python 2 используетсяD.__mro__), чтобы просмотретьDMRO дляMROинформацию)

  • Подробный анализ кода выглядит следующим образом:

    class A:
        def __init__(self):
            self.n = 2
    
        def add(self, m):
            # 第四步
            # 来自 D.add 中的 super
            # self == d, self.n == d.n == 5
            print('self is {0} @A.add'.format(self))
            self.n += m
            # d.n == 7
    
    
    class B(A):
        def __init__(self):
            self.n = 3
    
        def add(self, m):
            # 第二步
            # 来自 D.add 中的 super
            # self == d, self.n == d.n == 5
            print('self is {0} @B.add'.format(self))
            # 等价于 suepr(B, self).add(m)
            # self 的 MRO 是 [D, B, C, A, object]
            # 从 B 之后的 [C, A, object] 中查找 add 方法
            super().add(m)
    
            # 第六步
            # d.n = 11
            self.n += 3
            # d.n = 14
    
    class C(A):
        def __init__(self):
            self.n = 4
    
        def add(self, m):
            # 第三步
            # 来自 B.add 中的 super
            # self == d, self.n == d.n == 5
            print('self is {0} @C.add'.format(self))
            # 等价于 suepr(C, self).add(m)
            # self 的 MRO 是 [D, B, C, A, object]
            # 从 C 之后的 [A, object] 中查找 add 方法
            super().add(m)
    
            # 第五步
            # d.n = 7
            self.n += 4
            # d.n = 11
    
    
    class D(B, C):
        def __init__(self):
            self.n = 5
    
        def add(self, m):
            # 第一步
            print('self is {0} @D.add'.format(self))
            # 等价于 super(D, self).add(m)
            # self 的 MRO 是 [D, B, C, A, object]
            # 从 D 之后的 [B, C, A, object] 中查找 add 方法
            super().add(m)
    
            # 第七步
            # d.n = 14
            self.n += 5
            # self.n = 19
    
    d = D()
    d.add(2)
    print(d.n)

    Схема процедуры вызова выглядит следующим образом:

    D.mro() == [D, B, C, A, object]
    d = D()
    d.n == 5
    d.add(2)
    
    class D(B, C):          class B(A):            class C(A):             class A:
        def add(self, m):       def add(self, m):      def add(self, m):       def add(self, m):
            super().add(m)  1.--->  super().add(m) 2.--->  super().add(m)  3.--->  self.n += m
            self.n += 5   <------6. self.n += 3    <----5. self.n += 4     <----4. <--|
            (14+5=19)               (11+3=14)              (7+4=11)                (5+2=7)

Теперь вы знаете, почему d.n равно 19 после d.add(2), верно? ;)

Источник статьи: https://mozillazg.com/2016/12/python-super-is-not-as-simple-as-you-thought.html