2016-02-25 18 views
11

Tôi đã đọc Lập trình Python chuyên gia có ví dụ về đa kế thừa. Tác giả cuốn sách đã giải thích nhưng tôi không hiểu nó, vì vậy tôi muốn có một cái nhìn khác.Một đối tượng được tạo hai lần trong Python

Ví dụ cho thấy đối tượng B được tạo hai lần!

Xin vui lòng cho tôi một lời giải thích trực quan.

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> 

Tác giả cuốn sách cho biết:

Điều này xảy ra do sự A.__init__(self) cuộc gọi, được thực hiện với sự dụ C, do đó làm cho super(A, self).__init__() gọi B 's constructor

Các mà từ đó tôi không hiểu ý tưởng của mình là cách gọi A.__init__(self) sẽ thực hiện super(A, self).__init__() gọi hàm tạo B

+0

Tại sao bạn không sử dụng 'siêu phương thức init của 'in C '? –

+0

Tôi chỉ muốn hiểu một đối tượng được tạo ra bằng Python như thế nào. – Bryan

+3

Nếu bạn nghĩ về lệnh gọi 'super' có nghĩa là 'gọi phương thức tiếp theo trong MRO', thay vì 'gọi phương thức của lớp cha mẹ', thì hành vi này sẽ có ý nghĩa hơn. Trong trường hợp này, lý do bạn thấy B được in hai lần là vì siêu sắp xếp cho init của B được gọi (bởi init của A), và vì vậy khi bạn gọi B.init từ C, bạn sẽ nhận được cuộc gọi thứ hai. –

Trả lời

8

super() chỉ có nghĩa là "tiếp theo trong dòng", trong đó dòng là mro['C', 'A', 'B', 'object']. Vì vậy, tiếp theo trong dòng cho AB.

Mro được tính theo thuật toán được gọi là C3 linearization. Khi bạn sử dụng super(), Python chỉ cần thực hiện theo thứ tự này. Khi bạn viết lớp học của bạn A bạn chưa biết lớp nào sẽ là lớp tiếp theo. Chỉ sau khi bạn tạo ra lớp học của bạn C với nhiều thừa kế và chạy chương trình của bạn, bạn sẽ nhận được mro và "biết" những gì sẽ được tiếp theo cho A.

Ví dụ bạn có nghĩa là:

C() gọi __init__() của C, trong đó nó gọi là __init__() của A. Bây giờ, A sử dụng super() và tìm thấy B trong mro, do đó nó gọi là __init__() của B. Tiếp theo, số __init__() của số C gọi lại số __init__() của B.

Calling super() trong __init__() tạo ra một MRO khác nhau và tránh các cuộc gọi đôi với __init__() của 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__() 

Sử dụng:

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

tôi vẫn không hiểu, bạn có thể giải thích thêm về MRO của – danidee

+0

C là '[' C ',' A ',' B ',' đối tượng '] '. C gọi 'A .__ init __()', Một cuộc gọi 'super()', trong MRO của C (ở đâu 'A .__ init __()' được gọi) tiếp theo trong dòng là 'B', do đó, 'in' B'' được gọi là trong A's super() và trong 'B .__ init __()' –

1

Hãy thay đổi mã một chút và thay thế __init__ với doit chỉ để chắc chắn rằng hành vi này là chung chung và không liên quan đến __init__.

Hãy cũng thêm sản lượng hơn để xem chính xác những gì sẽ xảy ra:

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() 

Sản lượng này sẽ:

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>> 

Bạn thấy đấy, mà self thực sự là C đối tượng ở khắp mọi nơi, vì vậy khi bạn nhấn A.doit , bạn thực sự có <super: <class 'A'>, <C object>>.

nào chuyển thành:

cho đối tượng C gọi các doit phương pháp của (siêu) bên cạnh lớp sau khi A từ danh sách MRO

Và các lớp tiếp theo trong MRO sau AB, vì vậy chúng tôi sẽ gọi số B.doit().

Kiểm tra cũng mã này:

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) 

đây thay vì A.doit(self) tôi sử dụng super(A, self).doit() trực tiếp và nó cũng dẫn đến B.doit() cuộc gọi, đây là kết quả:

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>> 
Các vấn đề liên quan