2010-09-17 2 views
9

, во-первых, позвольте мне процитировать чуток эссе от «Эксперт Python Программирование» книги:смесительные супер и классические звонки в Python

В следующем примере C класса, который называет свои базовые классы с использованием метода __init__ будет , чтобы класс B был вызван дважды!

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" 
     A.__init__(self) 
     B.__init__(self) 

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

и, наконец, вот объяснение того, что происходит здесь:

Это происходит из-за A .__ инициализации __ (само) вызова, который сделан с экземпляром C, таким образом создавая супер (A, self) .__ init __() вызывает конструктор B. Иными словами, super должен использоваться во всей иерархии классов. Проблема в том, что иногда часть этой иерархии находится в стороннем коде.

Я понятия не имею, почему «super(A, self).__init__() вызывает конструктор B». Пожалуйста, объясните этот момент. Большое спасибо.

ответ

4

Документация super говорит, что:

Возвращение прокси-объект, который метод делегатов призывает родителей или родственного класса типа. Это полезно для доступа к унаследованным методам, которые были переопределены в классе. Порядок поиска такой же, как и для getattr(), за исключением того, что сам тип пропускается.

При выполнении A.__init__(self) изнутри Csuper(A, self) вернется <super: <class 'A'>, <C object>>. Поскольку экземпляр C (<C object>), все классы в иерархии наследования C собраны. И звонок __init__ выдается на всех из них. Следовательно, вы видите, что «B» получает вызов дважды.

Чтобы проверить это, добавьте еще один класс 'Z' и дайте 'C' наследовать от 'Z'. Посмотрите, что произойдет.

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

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

В этом случае A будем называть B и Z. B будет звонить по телефону Z.

9

Чтобы понять это поведение, вы должны понимать, что super вызывает не базовый класс, а ищет следующий метод сопоставления по порядку в __mro__. Таким образом, вызов super(A, self).__init__() рассматривает __mro__ == ['C', 'A', 'B', 'object'], видит B как следующий класс с методом сопоставления и вызывает метод (конструктор) B.

Если изменить C к

class C(A,B): 
    def __init__(self): 
     print "C1" 
     A.__init__(self) 
     print "C2" 
     B.__init__(self) 
     print "C3" 

вы получите

MRO: ['C', 'A', 'B', 'object'] 
C1 
A 
B 
C2 
B 
C3 

, который показывает, как конструктор A вызовов B.

+1

спасибо. один вопрос - верно ли, что, когда «super (A, self) .__ init __()» вызывается в конструкторе класса A, его аргумент «self» равен нашему экземпляру C, который мы только что создали? – varnie

+2

@varnie: да. Вы можете 'напечатать супер (A, self)' внутри '' __init __() 'метод, чтобы увидеть, что' self' действительно является экземпляром 'C'. –

Смежные вопросы