Объект создается дважды в 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>>