Объект создается дважды в Python

Я прочитал программу программирования экспертов Python, которая имеет пример для многоуровневого. Автор книги объяснил, но я этого не понял, поэтому хотел бы получить другое представление.

В примере показано, что объект B создается два раза!

Не могли бы вы дать мне интуитивное объяснение.

In [1]: class A(object):
    ...:     def __init__(self):
    ...:         print "A"
    ...:         super(A, self).__init__()

In [2]: class B(object):
    ...:     def __init__(self):
    ...:         print "B"
    ...:         super(B, self).__init__()

In [3]: class C(A,B):
    ...:     def __init__(self):
    ...:         print "C"
    ...:         A.__init__(self)
    ...:         B.__init__(self)

In [4]: print "MRO:", [x.__name__ for x in C.__mro__]
MRO: ['C', 'A', 'B', 'object']

In [5]: C()
C
A
B
B
Out[5]: <__main__.C at 0x3efceb8>

Автор книги сказал:

Это происходит из-за вызова A.__init__(self), который выполняется с помощью C, создавая super(A, self).__init__() вызов B конструктор

Точка, из которой я не понял, как вызов A.__init__(self) сделает super(A, self).__init__() вызов B конструктор

Ответ 1

super() просто означает "next in line", где строка mro ['C', 'A', 'B', 'object']. Итак, следующая строка для A равна B.

mro вычисляется по алгоритму линеаризация C3. Когда вы используете super(), Python просто идет по этому порядку. Когда вы пишете свой класс A, вы еще не знаете, какой класс будет в очереди. Только после создания класса C с множественным наследованием и запуска вашей программы вы получите mro и "знаете", что будет дальше для A.

В вашем примере это означает:

C() вызывает __init__() из C, в котором он вызывает __init__() of A. Теперь A использует super() и находит B в mro, поэтому он вызывает __init__() of B. Затем __init__() из C снова вызывает __init__() of B.

Вызов super() в __init__() создает другой mro и позволяет избежать двойного вызова __init__() из B.

from __future__ import print_function

class A(object):
    def __init__(self):
        print("A")
        super(A, self).__init__()

class B(object):
    def __init__(self):
        print("B")
        super(B, self).__init__()

class C(A,B):
    def __init__(self):
        print("C")
        super(C, self).__init__()

Использование:

>>> C.mro()
[__main__.C, __main__.A, __main__.B, object]
>> C()
C
A
B

Ответ 2

Немного измените код и замените __init__ на doit, чтобы убедиться, что поведение является общим и не связано с __init__.

Пусть также добавляет больше результатов, чтобы увидеть, что именно происходит:

class A(object):
     def doit(self):
         print "A", self, super(A, self)
         super(A, self).doit()

class B(object):
     def doit(self):
         print "B", self, super(B, self)

class C(A,B):
     def doit(self):
         print "C", self
         A.doit(self)
         B.doit(self)

print "MRO:", [x.__name__ for x in C.__mro__]
#MRO: ['C', 'A', 'B', 'object']

C().doit()

Это выведет:

C <__main__.C object at ...>
A <__main__.C object at ...> <super: <class 'A'>, <C object>>
B <__main__.C object at ...> <super: <class 'B'>, <C object>>
B <__main__.C object at ...> <super: <class 'B'>, <C object>>

Вы видите, что self на самом деле C объект повсюду, поэтому, когда вы нажимаете A.doit, у вас на самом деле есть <super: <class 'A'>, <C object>>.

Это означает:

для объекта C вызывает метод doit следующего (супер) класса после A из списка MRO

И следующий класс в MRO после A равен B, поэтому мы вызываем B.doit().

Также проверьте этот код:

class C(A,B):

     def doit_explain(self):
         print "C", self
         # calls B.doit()
         super(A, self).doit()
         print "Back to C"
         # calls A.doit() (and super in A also calls B.doit())
         super(C, self).doit()
         print "Back to C"
         # and just B.doit()
         B.doit(self)

Здесь вместо A.doit(self) я использую super(A, self).doit() напрямую, а также вызывает вызов B.doit(), вот результат:

C <__main__.C object at ...>
B <__main__.C object at ...> <super: <class 'B'>, <C object>>
Back to C
A <__main__.C object at ...> <super: <class 'A'>, <C object>>
B <__main__.C object at ...> <super: <class 'B'>, <C object>>
Back to C
B <__main__.C object at ...> <super: <class 'B'>, <C object>>