2016-02-25 2 views
11

Я прочитал программу программирования экспертов 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 «s конструктор

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

+0

Почему вы не используете 'супер 'в методе init C? –

+0

Я просто хочу понять, как объект создается в Python. – Bryan

+3

Если вы считаете, что вызов 'super' означает« вызов следующего метода в MRO », а не« вызов метода моего родительского класса », тогда это поведение должно иметь больше смысла. В этом случае причина, по которой вы дважды печатаете B, состоит в том, что супер уже организует вызов B для init (по инициативе A), и поэтому, когда вы явно вызываете B.init из C, вы получаете второй вызов. –

ответ

8

super() просто означает «следующий в строке», где находится линия mro['C', 'A', 'B', 'object']. Итак, следующая строка для A - B.

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

Для примера это означает:

C() называет __init__() из C, в котором он вызывает __init__() из A. Теперь A использует super() и находит B в mro, поэтому он вызывает __init__() из B. Затем __init__() из C снова вызывает __init__()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 
+1

Я все еще не понимаю, что вы можете объяснить больше – danidee

+0

C MRO is '['C', 'A', 'B', 'object']'. C вызывает 'A .__ init __()', A вызывает 'super()', в CRO CRO (где вызывается '.__ init __()'), следующий в строке - 'B', поэтому вызывается 'print 'B'' в супер() и в 'B .__ init __()' –

1

Давайте изменим код немного и заменить __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 после AB, поэтому мы в конечном итоге вызываем 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>> 
Смежные вопросы