Python的新式類和經典類

問題

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

擴展

  1. 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__()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章