問題
在Python中定義類時,我們經常看到兩種寫法:
class PersonOne:
name = "person one"
class PersonTwo(object):
name = "person two"
在Python2.X中,第一種寫法稱之爲經典類,第二種寫法稱之爲新式類。Python2.2之前只能支持第一種寫法,在Python2.2到Python2.7,兩種寫法都可以,但是不同寫法定義出來的類是不一樣的;在Python3.X中兩種寫法都可以,而且定義出來的類是完全一樣的,都是新式類,可以理解爲和Java一樣,Python3.0之後Object已經作爲所有類的基類,因此是否顯示指明已經不重要。因此,如果你是使用Python3.X的版本,完全可以無視這個問題,怎麼寫都行;但是如果不是就需要搞清楚區別了。
使用新式類和經典類的區別
個人理解,兩種定義核心的區別就是定義類的MRO是不同的,在多重繼承中,MRO直接決定了方法的調用順序,因此會產生很大的影響。
-
經典類的MRO
經典類的MRO的生成時基於深度優先遍歷算法的,以下面的繼承關係爲例,生成的MRO爲:[D,B,A,C],因此,在調用test方法時,按照此順序查找必然先找到A中的test方法,但是這其實是不太不合理的。class A: def test(self): print('in a test') class B(A): pass class C(A): def test(self): print('in c test') class D(B, C): pass if __name__ == '__main__': d = D() d.test()
-
新式類的MRO
在新式類中,MRO的生成時基於C3算法的,關於C3算法計算過程參見鏈接,核心就是Merge函數的計算過程,有的文章中直接說是廣度優先其實並不準確。此處生成的類D的MRO爲[D,B,C,A],那麼,按照這個順序調用test方法自然找到的是C中的test。
擴展
-
super的調用關係
在Python3中,我們經常使用到super方法,在單繼承中沒有什麼疑問,直接調用super方法自然是訪問其唯一的父類。但是再多繼承中,又是如何決定調用哪一個父類的呢?這個還是和MRO有關。
準確來說,super方法的調用方式爲super(ClassName, self).func(),那麼此處的func方法就是屬於MRO中ClassName下一個類。如果確實沒有參數,即super().func(),等價於super(MRO中的第一個類, self).func()
還是以上面的繼承關係爲例,重寫類D的__init__()方法,第一個super調用的是B,第二個是C。class D(B, C): def __init__(self): print("C") super().__init__() super(B, self).__init__()