Python - 對多繼承以及super的一些了解


  Python支持多繼承,與C++一樣都會出現一種問題:子類繼承的多個父類又繼承了同一個父類,這時就有可能會出現父類構造方法被調用多次的情況。關於這個問題,我找了一些資料,雖然沒有親自全部驗證,這里我總結一下自己對這個問題的看法。

  Python和C++的關於這個問題的解決方案不太一樣,當然Python還要看它的版本。

  C++用的方案是引入了虛繼承的語法避免同一個類被構造了多次。

  Python用的方法是MRO(method resolution order,方法解析順序) 。在在Python2.3之前,MRO的實現是基於DFS的,而在Python2.3以后MRO的實現是基於C3算法。找到的資料解釋了一下更換算法的原因:

  為什么采用C3算法
  C3算法最早被提出是用於Lisp的,應用在Python中是為了解決原來基於深度優先搜索算法不滿足本地優先級,和單調性的問題。
  本地優先級:指聲明時父類的順序,比如C(A,B),如果訪問C類對象屬性時,應該根據聲明順序,優先查找A類,然后再查找B類。
  單調性:如果在C的解析順序中,A排在B的前面,那么在C的所有子類里,也必須滿足這個順序。
------------------------------新式類和舊式類中查找屬性的順序不同-------------------------------------
   在新式類中,查找一個要調用的函數或者屬性的時候,是廣度優先搜搜的。
  在舊式類當中,是深度優先搜索的。如下圖所示:
  來一個例子:
  
 1 # -*- coding:utf-8 -*-
 2 
 3 class D(object):
 4     def foo(self):
 5         print "class D"
 6 
 7 class B(D):
 8     pass
 9 
10 class C(D):
11     def foo(self):
12         print "class C"
13 
14 class A(B, C):
15     pass
16 
17 f = A()
18 f.foo()
  例子中定義D類的時候,D是新式類,所以D的所有子類都是新式類。
  A的實例對象f在調用foo函數的時候,根據廣度優先搜索原則,調用的是C類里面的foo函數。
  上面的代碼輸出class C
  如果定義D類的時候直接class D,而不是class D(object),那么上述代碼就該輸出class D了。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

  如果我用的是super來解決多繼承的初始化問題的話,那么到底是怎么初始化的呢?

  這是我一開始想到的一個問題:

  

 1 class A(object):
 2     def __init__(self,a):
 3         print a
 4         
 5 class B(object):
 6     def __init__(self,a,b):
 7         print a+b
 8         
 9 class C(A,B):
10     def __init__(self):
11          super(C,self).__init__(?)
12 
13 obj = C()   

  第十一行的那個'?'的位置到底是填是什么進去才對呢?

  我一開始認為因為是多繼承多以需要初始化父類的時候A,B都需要初始化,那么問題來了,super(C,self).__init__(?)怎么寫?

  我試了一下:

  ① ? = 1      -->  輸出1

  ② ? = 1,2       --> TypeError: __init__() takes exactly 2 arguments (3 given)

  定義C的時候改成class(B,A)

  ③ ? = 1      --> TypeError: __init__() takes exactly 3 arguments (2 given)

  ④ ? =1,2        --> 輸出3

 

  再來看另一段代碼:

 1 class A(object):
 2     def func(self):
 3         print 1
 4          
 5 class B(object):
 6     def __init__(self):
 7         print 2
 8         
 9 class C(A,B):
10     def __init__(self):
11          super(C,self).__init__()
12 
13 obj = C()   
14 #The output is 3

  說明什么問題?在調用super(classname,self).__init__()的時候應該調用在繼承的父類列表里面有實現__init__()這個方法而且最靠左邊的那個父類的構造方法,而且這個__init__(?)的'?'一定要與父類列表的里面第一個有構造方法的父類的構造方法簽名一樣才可以。

 

  所以如果需要對所有父類都進行一遍初始化,還是使用類通過類名調用未綁定的初始化方法好(我說這一句話是因為我還不是很了解super)。

  說一下super的使用:

    super( classname,對象(一般情況是self) ) 返回的是一個super對象,你可以把它當作父類列表里面有實現__init__()這個方法而且最靠左邊的那個父類的一個對象就可以了。

 

  參考文章地址:

    http://blog.csdn.net/imzoer/article/details/8737642

    http://blog.csdn.net/zyflying/article/details/8636006

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM