Python: 你不知道的 super

2021-03-02 程式設計師技術

點擊上方藍色字體關注「程式設計師共讀」

來源:geekvi

來源:segmentfault.com/a/1190000007426467

super() 的入門使用

在類的繼承中,如果重定義某個方法,該方法會覆蓋父類的同名方法,但有時,我們希望能同時實現父類的功能,這時,我們就需要調用父類的方法了,可通過使用 super 來實現,比如:

class Animal(object):

    def __init__(self, name):

        self.name = name

    def greet(self):

        print 'Hello, I am %s.' % self.name

 

class Dog(Animal):

    def greet(self):

        super(Dog, self).greet()   # Python3 可使用 super().greet()

        print 'WangWang...'

在上面,Animal 是父類,Dog 是子類,我們在 Dog 類重定義了 greet 方法,為了能同時實現父類的功能,我們又調用了父類的方法,看下面的使用:

>>> dog = Dog('dog')

>>> dog.greet()

Hello, I am dog.

WangWang..

super 的一個最常見用法可以說是在子類中調用父類的初始化方法了,比如:

class Base(object):

    def __init__(self, a, b):

        self.a = a

        self.b = b

 

class A(Base):

    def __init__(self, a, b, c):

        super(A, self).__init__(a, b)  # Python3 可使用 super().__init__(a, b)

        self.c = c

深入 super()

看了上面的使用,你可能會覺得 super 的使用很簡單,無非就是獲取了父類,並調用父類的方法。其實,在上面的情況下,super 獲得的類剛好是父類,但在其他情況就不一定了,super 其實和父類沒有實質性的關聯

讓我們看一個稍微複雜的例子,涉及到多重繼承,代碼如下:

class Base(object):

    def __init__(self):

        print "enter Base"

        print "leave Base"

 

class A(Base):

    def __init__(self):

        print "enter A"

        super(A, self).__init__()

        print "leave A"

 

class B(Base):

    def __init__(self):

        print "enter B"

        super(B, self).__init__()

        print "leave B"

 

class C(A, B):

    def __init__(self):

        print "enter C"

        super(C, self).__init__()

        print "leave C"

其中,Base 是父類,A, B 繼承自 Base, C 繼承自 A, B,它們的繼承關係如下:


現在,讓我們看一下使用:

>>> c = C()

enter C

enter A

enter B

enter Base

leave Base

leave B

leave A

leave C

如果你認為 super 代表『調用父類的方法』,那你很可能會疑惑為什麼 enter A 的下一句不是 enter Base 而是 enter B。原因是,super 和父類沒有實質性的關聯,現在讓我們搞清 super 是怎麼運作的。

MRO 列表

事實上,對於你定義的每一個類,Python 會計算出一個方法解析順序(Method Resolution Order, MRO)列表,它代表了類繼承的順序,我們可以使用下面的方式獲得某個類的 MRO 列表:

>>> C.mro()   # or C.__mro__ or C().__class__.mro()

[__main__.C, __main__.A, __main__.B, __main__.Base, object]

那這個 MRO 列表的順序是怎麼定的呢,它是通過一個 C3 線性化算法來實現的,這裡我們就不去深究這個算法了,感興趣的讀者可以自己去了解一下,總的來說,一個類的 MRO 列表就是合併所有父類的 MRO 列表,並遵循以下三條原則:

子類永遠在父類前面

如果有多個父類,會根據它們在列表中的順序被檢查

如果對下一個類存在兩個合法的選擇,選擇第一個父類

super 原理

super 的工作原理如下:

def super(cls, inst):

    mro = inst.__class__.mro()

    return mro[mro.index(cls) + 1]

其中,cls 代表類,inst 代表實例,上面的代碼做了兩件事:

當你使用 super(cls, inst) 時,Python 會在 inst 的 MRO 列表上搜索 cls 的下一個類。

現在,讓我們回到前面的例子。

首先看類 C 的 __init__ 方法:

super(C, self).__init__()

這裡的 self 是當前 C 的實例,self.__class__.mro() 結果是:

[__main__.C, __main__.A, __main__.B, __main__.Base, object]

可以看到,C 的下一個類是 A,於是,跳到了 A 的 __init__,這時會列印出 enter A,並執行下面一行代碼:

super(A, self).__init__()

注意,這裡的 self 也是當前 C 的實例,MRO 列表跟上面是一樣的,搜索 A 在 MRO 中的下一個類,發現是 B,於是,跳到了 B 的 __init__,這時會列印出 enter B,而不是 enter Base。

整個過程還是比較清晰的,關鍵是要理解 super 的工作方式,而不是想當然地認為 super 調用了父類的方法。

小結

參考資料

調用父類方法 — python3-cookbook 2.0.0 文檔

理解 Python super – laike9m’s blog

python super() – 漩渦鳴人 – 博客園

Python:super函數 | Hom

Python’s super() considered super! | Deep Thoughts by Raymond Hettinger

Python super() inheritance and needed arguments – Stack Overflow


相關焦點

  • Python新式類的方法解析順序MRO與Super
    新式類:廣度優先的C3算法實現(拓撲排序) BFS python2.3及以後python 用MRO的目的是什麼解決多重繼承的二義性的問題(二義性:父類存在同名函數的時候會產生二義性)為python調用一個類或者實例方法的時候提供查找順序經典類的方法解析順序深度優先算法:
  • Python中super( )的用法
    因此,自Python 2.2開始,Python添加了一個關鍵字super,來解決這個問題。下面是Python3.5的官方文檔說明:https://docs.python.org/3.5/library/functions.html?
  • 使用Python Super()為類提供繼承支持
    super()能為你做什麼?使用super()調用以前構建的方法可以使您無需在子類中重寫這些方法,並允許您使用最少的代碼更改來替換超類。單繼承中的super()如果您不熟悉面向對象的編程概念,繼承可能是一個不熟悉的術語。 繼承是面向對象編程中的一個概念,其中類從另一個類派生(或繼承)屬性和行為,而無需再次實現它們。
  • Python多繼承、super與MRO算法
    任何面向對象程式語言都會支持繼承,Python也不例外。但Python語言卻是少數幾個支持多繼承的面向對象程式語言(另一個著名的支持多繼承的程式語言是C++)。本文將深入闡述Python多繼承中經常用到的super,並且會展示一個你所不知道的super。相信繼承的概念大家一定不會陌生。
  • 【python勸退指南】重複執行,才是程序最大的魅力!
    同時也借 Boolean (布爾類型) 講解了python中的條件語句。(本節內容較多,請細細品味)        這節我們來借 List (列表) 類型,來講解一下python中的複讀機:循環語句。某一列數據        我們所了解的 基本表格 ,其結構一般都是由 表頭 和 數據 組成。
  • 使用Python super方法為您的類增強
    super() 在單一繼承中如果您不熟悉面向對象的編程概念,繼承可能是一個不熟悉的術語。繼承是面向對象編程中的一個概念,其中類從另一個類派生(或繼承)屬性和行為,而無需再次實現它們。__init__(length, length)在這裡,你已經使用super()調用__init__()的的Rectangle類,允許你使用它的Square類不重複的代碼。
  • python | 關於多重繼承那些事
    和 c++ 一樣 ,在 python 中一個類能繼承自不止一個父類 ,這叫做 python 的多重繼承(Multiple Inheritance )。多重繼承的語法與單繼承類似 。class SubclassName(BaseClass1, BaseClass2, BaseClass3, ...)
  • 好好理解 Python 面向對象中的多繼承和super
    ok,以下是 VIP 基礎階段的試看文:Python快速入門 | 好好理解 Python 面向對象中的多繼承和super咱們上一篇講到了繼承,說到了子類和父類之間的關係,父類也叫作基類、超類,也就是 super class ,上次我們說要在子類使用父類定義的東西,就需要用到 super 方法,有些朋友不太理解:不是說子類繼承了父類,就直接都擁有了父類的東西了麼
  • 全面深入理解 Python 類與對象
    Python面向對象編程這裡就不多做代碼重寫,去原連結查看學習即可。數據封裝與私有屬性私有屬性,也叫私有方法,是以雙下劃線開頭的命名變量。無法通過實例方法和子類來調用的,只能通過類中寫的方法來調用。但是這樣的封裝並不是完全安全的,比如你看下面的代碼:class magic: __user = '浪子'm = magicprint(m._magic__user)這樣能直接調用user,說白了這是一個小技巧python把私有變量偷偷隱藏起來變成了這樣子。
  • python面試輕鬆搞定,助你搞定面試官,內附15題及答案(08期)
    答:tuple,可以說是不可變的 list,訪問方式還是通過索引下標的方式。當你明確定義個tuple是,如果僅有一個元素,必須帶有,例如:(1,)。當然,在 2.7 以後的版,python 裡還增加了命名式的 tuple!
  • Python中你不知道的事:多繼承和類的方法,以及什麼是類的特殊方法
    我們依然可以調用方法得出結果1.多繼承輸出結果python 給繼承關係排列了一個順序,我們將它稱之為繼承樹。用print(D.輸出的結果super 在有重複的類名的時候直接調用方法。我們還可以用issubclass() 判斷是不是 子類,記住一定要寫類名。print(issubclass(C,A ) )以此種形式進行查看。
  • 機器學習如何從Python 2遷移到Python 3
    從類型提示(運行前)到類型檢查(運行時)默認情況下,函數的注釋對於代碼的運行是沒有影響的,它只是幫你指出每段代碼所要做的工作。在代碼運行階段,很多時候類型提示工具是不起作用的。這種情況你可以使用 enforce 等工具,強制性對代碼進行類型檢查,同時也可以幫助你調試代碼。
  • python他律筆記系列二
    和c/c++語言有較大不一樣的點for variable in sequence: statementelse: statement可以直接通過for語言進行輸出or通過range()函數進行一個匹配range 函數與切片的使用用法是一致的 不過多贅述break 和continue 值得注意的是break跳出後對應的else也不再適用
  • Python基礎知識
    ● 輸出:使用 printPython2 和 python3 中的 print 區別在於,python3 把 print 作為了一個內置函 數,使用時必須加括號,而 python2 可加可不加。對象的屬性是指它所知道的,而對象的方法是 指它所能做到的 ● 類(class)是一個用來構建特定類型對象的藍圖、或模版、或一套指令。每 一個對象都由類構建而來。每一個類的設計和編程都應該用來完成一件,並 且只能是一件事情 ● 實例(instance)是指由某個特定的類構建而來的特定的對象。它被賦值給 一個用來訪問實例的所有屬性和方法的引用變量。
  • 【Python大神秘籍Top10】這些竅門99%的人都不知道
    創建對象後,python解釋器默認調用__init__()方法。無論主構造函數調用什麼,它都會被傳遞。__init__幾乎在Python類定義中普遍使用。如果__new__和__init__構成了對象的構造函數,__ del__就是析構函數。當刪除一個對象時,python解釋器也會默認調用__del__()方法。
  • 這些Python代碼技巧,你肯定還不知道
    本文或許能夠讓你學到一些新技巧。請點擊這裡:https://pypi.org/project/emoji/$ pip install emoji別以為我不知道你會偷偷試它→→詞典對象前面的雙星號可以讓你把該詞典的內容作為命名參數輸入到函數中。詞典的秘鑰是參數名,值是傳遞給函數的值。你甚至不需要稱它為 kwargs!
  • 你不知道的Python小技巧,趕快收藏吧!
    python沒有使用語法強制定義常量,也就是說,python中定義常量本質上就是變量。如果非要定義常量,變量名必須全大寫。如果是常量,那就沒必要更改,所以python就只制定了一個規範,而沒指定常量的語法,因此常量也是可以修改的,但不建議。在c語言中有專門的常量定義語法,const int age = 19 一旦定義 age 為常量,更改age即會報錯 。
  • 這25條極簡Python代碼,你還不知道
    可能有些你還不知道,但對你未來的Python項目很有用。# a = 4 b = 5a,b = b,a# print(a,b) >> 5,4讓我們通過交換兩個變量作為一個簡單的開始。這是最簡單、最直觀的方法之一,無需使用臨時變量或應用算術操作即可編寫。
  • 你可能不知道的 Python 技巧
    如果我們只想丟棄迭代器的開頭部分(在此例中是注釋),並且不知道有多少內容,那麼此方法很有用。例如,你不能將 IPv4Network 實例當成地址字符串——需要先使用 str 轉換它們。14、在Shell中調試程序崩潰如果你是一個拒絕使用 IDE,並在 Vim 或 Emacs 中進行編碼的人,那麼你可能會遇到這樣的情況:擁有在 IDE 中那樣的調試器會很有用。你知道嗎?
  • 2020Python工程師面試題更新啦!+高中生能看懂的Python爬蟲課程
    2)python會不會出現內存洩漏?原因是什麼?3)super 是幹嘛用的?在 Python2 和 Python3 使用,有什麼區別?為什麼要使用 super?4)UI 自動化,如何做集群?5)Python 中類方法、類實例方法、靜態方法有何區別?6)怎麼對含有驗證碼的功能進行自動化測試的?7)什麼是斷言?