Python中super()函數簡介及用法分享,pythonsuper

來源:互聯網
上載者:User

Python中super()函數簡介及用法分享,pythonsuper

首先看一下super()函數的定義:

super([type [,object-or-type]])

Return a **proxy object** that delegates method calls to a **parent or sibling** class of type.

返回一個代理對象, 這個對象負責將方法調用分配給第一個參數的一個父類或者同輩的類去完成.

parent or sibling class 如何確定?

第一個參數的__mro__屬性決定了搜尋的順序, super指的的是 MRO(Method Resolution Order) 中的下一個類, 而不一定是父類!

super()和getattr() 都使用__mro__屬性來解析搜尋順序, __mro__實際上是一個唯讀元組.

MRO中類的順序是怎麼排的呢?

實際上MRO列表本身是根據一種C3的線性化處理技術確定的, 理論說明可以參考這裡, 這裡只簡單說明一下原則:

在MRO中, 基類永遠出現在衍生類別的後面, 如果有多個基類, 基類的相對順序不變.

MRO實際上是對繼承樹做層序遍曆的結果, 把一棵帶有結構的樹變成了一個線性表, 所以沿著這個列表一直往上, 就可以無重複的遍曆完整棵樹, 也就解決了多繼承中的Diamond問題.

比如說:

class Root:  passclass A(Root):  passclass B(Root):  passclass C(A, B):  passprint(C.__mro__)# 輸出結果為:# (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Root'>, <class 'object'>)

super()實際返回的是一個代理的super對象!

調用super()這個構造方法時, 只是返回一個super()對象, 並不做其他的操作.

然後對這個super對象進行方法調用時, 發生的事情如下:

找到第一個參數的__mro__列表中的下一個直接定義了該方法的類, 並執行個體化出一個對象
然後將這個對象的self變數綁定到第二個參數上, 返回這個對象

舉個例子:

class Root:  def __init__(self):    print('Root')class A(Root):  def __init__(self):    super().__init__() # 等同於super(A, self).__init__()

在A的構造方法中, 先調用super()得到一個super對象, 然後向這個對象調用init方法, 這是super對象會搜尋A的__mro__列表, 找到第一個定義了__init__方法的類, 於是就找到了Root, 然後調用Root.__init__(self), 這裡的self是super()的第二個參數, 是編譯器自動填滿的, 也就是A的__init__的第一個參數, 這樣就完成對__init__方法調用的分配.

注意: 在許多語言的繼承中, 子類必須調用父類的構造方法, 就是為了保證子類的對象能夠填充上父類的屬性! 而不是初始化一個父類對象...(我之前就一直是這麼理解的..). Python中就好多了, 所謂的調用父類構造方法, 就是明明白白地把self傳給父類的構造方法, 我的小身子骨就這麼交給你了, 隨便你怎麼折騰吧:joy:

參數說明

super() -> same as super(__class__, <first argument>) # <first argument>指的是調用super的函數的第一個參數super(type) -> unbound super objectsuper(type, obj) -> bound super object; requires isinstance(obj, type)super(type, type2) -> bound super object; requires issubclass(type2, type) Typical use to call a cooperative superclass method:  class C(B):    def meth(self, arg):      super().meth(arg)  This works for class methods too:  class C(B):    @classmethod    def cmeth(cls, arg):      super().cmeth(arg)

如果提供了第二個參數, 則找到的父類對象的self就綁定到這個參數上, 後面調用這個對象的方法時, 可以自動地隱式傳遞self.
如果第二個參數是一個對象, 則isinstance(obj, type)必須為True. 如果第二個參數為一個類型, 則issubclass(type2, type)必須為True

如果沒有傳遞第二個參數, 那麼返回的對象就是Unbound, 調用這個unbound對象的方法時需要手動傳遞第一個參數, 類似於Base.__int__(self, a, b).

不帶參數的super()只能用在類定義中(因為依賴於caller的第二個參數), 編譯器會自動根據當前定義的類填充參數.
也就是說, 後面所有調用super返回對象的方法時, 第一個參數self都是super()的第二個參數. 因為Python中所謂的方法, 就是一個第一個參數為self的函數, 一般在調用方法的時候a.b()會隱式的將a賦給b()的第一個參數.

super()的兩種常見用法:

單繼承中, super用來指代隱式指代父類, 避免直接使用父類的名字
多繼承中, 解決Diamond問題 (TODO)

對物件導向的理解

其實我覺得Python裡面這樣的文法更容易理解物件導向的本質, 比Java中隱式地傳this更容易理解.

所謂函數, 就是一段代碼, 接受輸入, 返回輸出. 所謂方法, 就是一個函數有了一個隱式傳遞的參數. 所以方法就是一段代碼, 是類的所有執行個體共用的, 唯一不同的是各個執行個體調用的時候傳給方法的this 或者self不一樣而已.

構造方法是什麼呢? 其實也是一個執行個體方法啊, 它只有在對象產生了之後才能調用, 所以Python中__init__方法的參數是self啊. 調用構造方法時其實已經為對象分配了記憶體, 構造方法只是起到初始化的作用, 也就是為這段記憶體裡面賦點初值而已.

Java中所謂的靜態變數其實也就是類的變數, 其實也就是為類也分配了記憶體, 裡面存了這些變數, 所以Python中的類對象我覺得是很合理的, 也比Java要直觀. 至於靜態方法, 那就與對象一點關係都沒有了, 本質就是個獨立的函數, 只不過寫在了類裡面而已. 而Python中的classmethod其實也是一種靜態方法, 不過它會依賴於cls對象, 這個cls就是類對象, 但是只要想用這個方法, 類對象必然是存在的, 不像執行個體對象一樣需要手動的執行個體化, 所以classmethod也可以看做是一種靜態變數. 而staticmethod就是真正的靜態方法了, 是獨立的函數, 不依賴任何對象.

Java中的執行個體方法是必須依賴於對象存在的, 因為要隱式的傳輸this, 如果對象不存在這個this也沒法隱式了. 所以在靜態方法中是沒有this指標的, 也就沒法調用執行個體方法. 而Python中的執行個體方法是可以通過類名來調用的, 只不過因為這時候self沒辦法隱式傳遞, 所以必須得顯式地傳遞.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.