使用Python Super()為類提供繼承支持

2021-02-19 Python程式設計師

目錄

Python的super()函數概述

單繼承中的super()

super()能為你做什麼?

深入super()

多重繼承中的super()

多重繼承概述

方法解析順序

多重繼承替代方案

super()回顧

雖然Python不是一種純面向對象的語言,但它足夠靈活,功能強大,足以讓您使用面向對象的範例構建應用程式。 Python實現這一目標的方法之一是通過使用super()來支持繼承。

在本教程中,您將了解以下內容:

Python中的繼承概念

Python中的多重繼承

super()函數如何工作

單繼承中的super()函數如何工作

多繼承中的super()函數如何工作

Python的super()函數概述

如果您有使用面向對象語言的經驗,那麼您可能已經熟悉了super()的功能。

如果沒有,不要害怕!雖然官方文檔是相當技術性的,但在高級別上,super()允許繼承超類的子類訪問該超類中的方法。

super()單獨返回超類的臨時對象,然後允許您調用該超類的方法。

你為什麼要這樣做呢? 雖然其可能性超出了你的想像,但常見的用例是構建子類來擴展先前已構建的類的功能。

使用super()調用以前構建的方法可以使您無需在子類中重寫這些方法,並允許您使用最少的代碼更改來替換超類。

單繼承中的super()

如果您不熟悉面向對象的編程概念,繼承可能是一個不熟悉的術語。 繼承是面向對象編程中的一個概念,其中類從另一個類派生(或繼承)屬性和行為,而無需再次實現它們。

至少對我來說,在查看代碼時更容易理解這些概念,所以讓我們編寫描述繼承結構的類:

這裡有兩個相似的類:Rectangle和Square。

你可以使用它們如下:

在此示例中,您有兩個彼此相關的形狀:正方形是一種特殊的矩形。 但是,代碼並不反映這種關係,因此具有基本上重複的代碼。

通過使用繼承,您可以減少寫入的代碼量,同時反映矩形和正方形之間的真實世界關係:

在這裡,您使用了super()來調用Rectangle類的__init __(),允許您在Square類中使用它而不重複代碼。 如下所示,核心功能在進行更改後仍然存在:

在此示例中,Rectangle是超類,Square是子類。

因為Square和Rectangle .__ init __()方法非常相似,所以你可以使用super()從Square的方法中調用超類的.__ init __()方法(Rectangle .__ init __())。 這裡設置了.length和.width屬性,即使您只需要為Square構造函數提供單個長度參數。

當你運行它時,即使你的Square類沒有顯式地實現它,對.area()的調用將使用超類中的.area()方法並列印16. Square類從Rectangle繼承.area() 方法。

注意:要了解有關Python中繼承和面向對象概念的更多信息,請務必在Python 3中查看面向對象編程(OOP)。

super()能為你做什麼?

那麼super()在單繼承中能為你做什麼呢?

與其他面向對象語言一樣,它允許您在子類中調用超類的方法。 這種情況的主要用例是擴展繼承方法的功能。

在下面的示例中,您將創建一個繼承自Square的類Cube,並擴展.area()的功能(通過Square繼承自Rectangle類)以計算Cube實例的表面積和體積:

現在您已經構建了類,讓我們看一下邊長為3的立方體的表面積和體積:

注意:請注意,在我們上面的示例中,單獨的super()不會為您調用方法:您必須在代理對象本身上調用該方法。

在這裡,您已經為Cube類實現了兩種方法:.surface_area()和.volume()。這兩種計算都依賴於計算單個面的面積,因此您不必重新實現面積計算,而是使用super()來擴展面積計算。

另請注意,Cube類定義沒有.__ init __()。因為Cube繼承自Square,而.__ init __()並沒有為Cube做任何不同的事情,所以你可以跳過定義它,並且將自動調用超類(Square)的.__ init __()。

super()將委託對象返回給父類,因此您可以直接調用它所需的方法:super().area()。

這不僅使我們不必重寫面積計算方法,而且還允許我們在一個位置更改內部.area()邏輯。當你有一些繼承自一個超類的子類時,這尤其有用。

深入super()

在進入多重繼承之前,讓我們快速介紹一下super()的機制。

雖然上面(和下面)的示例在沒有任何參數的情況下調用super(),但super()也可以使用兩個參數:第一個是子類,第二個參數是作為該子類實例的對象。

首先,讓我們看兩個示例,通過使用已有的類來展示操作第一個變量可以做什麼:

在Python 3中,super(Square,self)調用等同於無參數的super()調用。 第一個參數指的是子類Square,而第二個參數指的是Square對象,在這種情況下,它是self。 您也可以使用其他類調用super():

在此示例中,您將Square設置為super()的子類參數,而不是Cube。 這導致super()開始在實例層次結構中的Square上方的一個級別搜索匹配方法(在本例中為.area()方法),在本例中為Rectangle類。

在此特定示例中,行為不會更改。 但想像一下Square還實現了一個你想要確保Cube不使用的.area()方法。 以這種方式調用super()可以讓你這樣做。

注意:雖然為了探索它在內部的工作方式,我們正在對super()的參數進行大量的調整,但要注意不要經常這樣做。

對於大多數用例,建議使用對super()的無參數調用,並且需要定期更改搜索層次結構可能表明更大的設計問題。

第二個參數怎麼樣?請記住,這是一個對象,它是用作第一個參數的類的實例。例如,isinstance(Cube,Square)必須返回True。

通過包含實例化對象,super()返回一個綁定方法:綁定到對象的方法,用來為方法提供對象的上下文,例如任何實例屬性。如果未包含此參數,則返回的方法只是一個函數,與對象的上下文無關。

有關綁定方法,未綁定方法和函數的更多信息,請閱讀其描述符系統上的Python文檔。

注意:從技術上講,super()不返回方法。它返回一個代理對象。這是一個對象,它將調用正確的類方法,而不需要另外創建一個對象。

多重繼承中的super()

現在您已經看過了單繼承的概述以及super()和單繼承的一些示例,您將進一步了解多重繼承的概述和一些示例,這些示例將演示多繼承如何工作以及super()如何啟用該功能。

多重繼承概述

還有另一個用例,其中super()確實有用,而且這個用例並不像單個繼承場景那樣常見。 除了單繼承之外,Python還支持多繼承,其中子類可以從多個不必繼承的超類(也稱為兄弟類)繼承。

我是一個非常直觀的人,我發現圖表對於理解這樣的概念非常有幫助。 下圖顯示了一個非常簡單的多繼承方案,其中一個類繼承自兩個不相關(兄弟)的超類:

為了更好地說明多重繼承的實際應用,下面是一些代碼供您動手實踐,展示如何從三角形和正方形構建一個右金字塔(帶有方形底座的金字塔):

注意:術語「傾斜高度」可能不熟悉,特別是如果你已經使用幾何類或編寫金字塔類已經有一段時間了。

傾斜高度是從物體底部中心(如金字塔)到其面部到該物體頂部的高度。您可以在WolframMathWorld上閱讀更多關於傾斜高度的信息。

此示例聲明了一個Triangle類和一個繼承Square和Triangle的RightPyramid類。

您將看到另一個使用super()的.area()方法,就像在單繼承中一樣,目的是用到在Rectangle類中一直定義的.perimeter()和.area()方法。

注意:您可能會注意到上面的代碼尚未使用Triangle類中的任何繼承屬性。後面的例子將充分利用Triangle和Square的繼承。

但問題是兩個超類(Triangle和Square)都定義了一個.area()。花一點時間思考在RightPyramid上調用.area()時會發生什麼,然後嘗試調用它,如下所示:

您是否猜測Python會嘗試調用Triangle.area()? 這是因為所謂的方法解析順序在起作用。

注意:我們怎麼注意到Triangle.area()被調用了,而不是像我們希望的那樣,Square.area()? 如果查看回溯的最後一行(在AttributeError之前),您將看到對特定代碼行的引用:

您可以將幾何類中的這個方法認為是三角形面積公式。 或者,如果你像我一樣,你可能已經滾動到Triangle和Rectangle類定義,並在Triangle.area()中看到了相同的代碼。

方法解析順序

方法解析順序(或MRO)告訴Python如何搜索繼承的方法。 當你使用super()時,這會派上用場,因為MRO會告訴你Python將使用super()以及以什麼順序調用的方法。

每個類都有一個.__ mro__屬性,允許我們檢查順序,所以讓我們這樣做:

這告訴我們首先在Rightpyramid中搜索方法,然後在Triangle中搜索,然後在Square中搜索,然後在Rectangle中搜索,然後,如果沒有找到,則在對象中搜索。

這裡的問題是解釋器在Square和Rectangle之前在Triangle中搜索.area(),並且在Triangle中找到.area()時,Python會調用它而不是你想要的那個。 因為Triangle.area()期望有.height和.base屬性,所以Python會拋出AttributeError。

幸運的是,您可以控制MRO的構建方式。 只需更改RightPyramid類的籤名,即可按所需順序進行搜索,方法將正確解析:

請注意,RightPyramid使用Square類中的.__ init __()進行部分初始化。 這允許.area()在對象上使用.length,如設計的那樣。

現在,您可以構建金字塔類,檢查MRO並計算表面積:

您可以看到MRO現在是您所期望的,並且您也可以檢查金字塔的表面積,這要歸功於.area()和.perimeter()。

不過,這裡仍然存在問題。為了簡單起見,我在這個例子中故意設置了一些錯誤:第一個,可以說最重要的是,我有兩個具有相同方法名稱和籤名的獨立類。

這會導致方法解析問題,因為將調用MRO列表中遇到的.area()的第一個實例。

當您使用具有多重繼承的super()時,必須設計您的類以進行協作。其中一點是確保您的方法是唯一的,以便通過籤名確保方法是唯一的 - 無論是使用方法名稱還是方法參數,在MRO中正確解析它們。

在這種情況下,為了避免對代碼進行徹底檢查,可以將Triangle類的.area()方法重命名為.tri_area()。這樣,area方法可以繼續使用類屬性而不是使用外部參數:

讓我們繼續在RightPyramid類中使用它:

這裡的下一個問題是代碼沒有像Square對象那樣的委託的Triangle對象,所以調用.area_2()會給我們一個AttributeError,因為.base和.height沒有任何值。

你需要做兩件事來解決這個問題:

使用super()調用的所有方法都需要調用其超類的該方法版本。 這意味著您需要將super().__ init __()添加到Triangle和Rectangle的.__ init __()方法中。

重新設計所有.__ init __()調用以獲取關鍵字字典。 請參閱下面的完整代碼。

此代碼中存在許多重要差異:

kwargs在某些地方被修改(例如RightPyramid .__ init __()): 這將允許這些對象的用戶僅使用對該特定對象有意義的參數來實例化它們。

在**kwargs之前設置命名參數:你可以在RightPyramid .__ init __()中看到這個。 這具有從**kwargs字典中去除特定鍵的簡潔效果,因此當它在MRO中最終進行到object時,**kwargs為空。

注意:跟蹤kwargs的狀態在這裡可能很棘手,所以這裡是一個.__ init __()調用表,顯示擁有該調用的類,以及該調用期間kwargs的內容:

現在,當您使用這些更新的類時,您有:

起作用了! 您已經使用super()成功追溯複雜的類層次結構,同時使用繼承和組合來創建具有最少重新實現的新類。

多重繼承替代方案

如您所見,多重繼承可能很有用,但也會導致非常複雜的情況和難以閱讀的代碼。 擁有整齊地從多個其他對象繼承所有東西的對象也很少見。

如果您發現自己開始使用多重繼承和複雜的類層次結構,那麼值得問問自己是否可以通過使用組合而不是繼承來實現更清晰,更易於理解的代碼。

通過組合,您可以從一個稱為mixin的專用簡單類中為您的類添加非常特定的功能。

由於本文主要關注繼承,因此我不會詳細介紹組合以及如何在Python中使用它,但這裡有一個使用VolumeMixin為我們的3D對象提供特定功能的簡短示例 - 在這種情況下,是一個體積計算:

在這個例子中,代碼被重新設計為包含一個名為VolumeMixin的mixin。 然後,Cube使用mixin並使Cube能夠計算其體積,如下所示:

這個mixin可以在任何需要計算體積的類中以相同的方式使用,並且公式area*height返回正確的結果。

super()回顧

在本教程中,您學習了如何使用super()為類增加繼承。 您的學習之旅從單繼承的回顧開始,然後展示了如何使用super()輕鬆調用超類方法。

然後,您了解了多重繼承如何在Python中工作,以及將super()與多重繼承相結合的技術。您還了解了Python如何使用方法解析順序(MRO)解析方法調用,以及如何檢查和修改MRO以確保在適當的時間調用適當的方法。

有關Python中面向對象編程和使用super()的更多信息,請查看以下資源:

英文原文:https://realpython.com/python-super/
譯者:javylee

相關焦點

  • 使用Python super方法為您的類增強
    雖然Python不僅僅是一種面向對象的語言,但它足夠靈活,功能強大,足以讓您使用面向對象的範例構建應用程式。Python實現這一目標的方法之一是支持繼承,它與之相關super()。__init__()方法非常相似,所以你可以簡單地通過使用來調用超類的.__init__()方法(Rectangle.__init__())。即使您只需要為構造函數提供單個參數,也會設置和屬性。
  • Python新式類的方法解析順序MRO與Super
    python3以前新式類:廣度優先 python2.2新式類:廣度優先的C3算法實現(拓撲排序) BFS python2.3及以後python 用MRO的目的是什麼解決多重繼承的二義性的問題(二義性:父類存在同名函數的時候會產生二義性)為python調用一個類或者實例方法的時候提供查找順序
  • Python多繼承、super與MRO算法
    任何面向對象程式語言都會支持繼承,Python也不例外。但Python語言卻是少數幾個支持多繼承的面向對象程式語言(另一個著名的支持多繼承的程式語言是C++)。本文將深入闡述Python多繼承中經常用到的super,並且會展示一個你所不知道的super。相信繼承的概念大家一定不會陌生。
  • Python中的類繼承
    歡迎關注 「小白玩轉Python」,發現更多 「有趣」繼承是面向對象編程中的一個概念,現有的類可以被一個新的類修改。現有的類稱為基類,新類稱為派生類。在本文中,我們將討論python中的類繼承。讓我們開始吧!
  • Python: 你不知道的 super
    () 的入門使用在類的繼承中,如果重定義某個方法,該方法會覆蓋父類的同名方法,但有時,我們希望能同時實現父類的功能,這時,我們就需要調用父類的方法了,可通過使用 super 來實現,比如:class Animal(object):    def __init__(self, name):        self.name = name
  • python中類的繼承和多態
    類的繼承類繼承語法:Class 派生類名(基類名): #基類名寫在括號裡派生類成員在繼承關係中,已有的,設計好的類稱為父類或基類,新設計的類稱為子類或派生類。派生類可以繼承父類的公有成員,但是不能繼承其私有成員。
  • Python中super( )的用法
    因此,自Python 2.2開始,Python添加了一個關鍵字super,來解決這個問題。下面是Python3.5的官方文檔說明:https://docs.python.org/3.5/library/functions.html?
  • python | 關於多重繼承那些事
    本文為第二篇讀者投稿 ,歡迎新老朋友有償投稿哦 。什麼是多重繼承繼承是面向對象編程的一個重要的方式 ,通過繼承 ,子類就可以擴展父類的功能 。和 c++ 一樣 ,在 python 中一個類能繼承自不止一個父類 ,這叫做 python 的多重繼承(Multiple Inheritance )。多重繼承的語法與單繼承類似 。
  • Python類的概念、定義、屬性、繼承
    在python3中如果繼承類是基類可以省略不寫。'''重點:學會定義類,了解屬性和方法,類的初始化和實例化。定義類時,這種方法可以使類對象實例按某種特定的模式生產出來。'''#類方法:'''後面的參數中第一個參數我們約定俗成的為self參數名,self代表的是在類實例化後這個實例對象本身。
  • Python類(4)類的繼承
    Python類(4)類的繼承類的繼承格式class 子類(基類): pass基類(父類): 被繼承者(先輩
  • 好好理解 Python 面向對象中的多繼承和super
    ok,以下是 VIP 基礎階段的試看文:Python快速入門 | 好好理解 Python 面向對象中的多繼承和super咱們上一篇講到了繼承,說到了子類和父類之間的關係,父類也叫作基類、超類,也就是 super class ,上次我們說要在子類使用父類定義的東西,就需要用到 super 方法,有些朋友不太理解:不是說子類繼承了父類,就直接都擁有了父類的東西了麼
  • Python中你不知道的事:多繼承和類的方法,以及什麼是類的特殊方法
    子類調用父類的方法我們並沒有實例化屬性,但是我們依然可以調用初始化的方法_init__()我們稱之為父類,定義的其他方法可以通過繼承基類來實現。super可以子類繼承父類的方法,實現其中的功能。可以同時繼承多個父類Python中可以有兩個以上的父類,當有A,B,C三個父類的時候,第三個父類可以同時繼承A與B兩類,C可以使用A與B中的屬性和方法。
  • Python 實例:類的繼承與重載
    __del__作用有限,一般很少使用。(1)類的繼承先來看Python類(新類)的繼承。使用一個最簡單的類和object類來比較一下。打開Putty,連接到Linux。那就將這個繼承於父類的函數重新寫入,做成一個符合需求的函數。最常用的函數重載就是__init__函數了。每個類都有__init__函數,實例化對象不同,每個類的__init__函數必定是不同的。但這個__init__函數都是繼承得來的,所以每個類(新類)的__init__函數都被重載了。還是以人類為例,如果是完全的繼承,沒有重載。至今的人類都還是用四腳走路。
  • 淺析 Python 的類、繼承和多態
    __y)不難看出,交互模式在列印 p 時其實是調用了 repr(p):>>> repr(p)'Point(10, 10)'_str_如果沒有提供 __str__,str() 預設使用 repr() 的結果。這兩者都是對象的字符串形式的表示,但還是有點差別的。
  • 全面深入理解 Python 類與對象
    _magic__user)這樣能直接調用user,說白了這是一個小技巧python把私有變量偷偷隱藏起來變成了這樣子。python的自省機制自省:通過一定的機制,查詢到對象的內部結構使用__dict__方法查詢類的屬性:class magic: '我是注釋' user = '浪子' def run(age): print(age)for x,y in magic.
  • python 類的繼承和派生
    什麼是繼承繼承是一種創建類的方法,在python中,一個類可以繼承來自一個或多個父類。原始類稱為基類或超類。__bases__(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)什麼時候用繼承假如已經有幾個類,而類與類之間有共同的變量屬性和函數屬性,那就可以把這幾個變量屬性和函數屬性提取出來作為基類的屬性。
  • 【4】類的繼承和多態-簡易的Python面向對象教程
    面向對象(2) - 實例方法 (本文) 面向對象(3) - 類屬性和類方法 面向對象(4) - 繼承和多態,以及一個綜合小遊戲案例
  • 理解Java繼承以及this和super用法,這一點要明白
    繼承的好處就是提高代碼的復用性和類和類之間產生關係,這也是三大特性其中之一多態的前提。子類繼承父類成員變量特點如果子類繼承了父類,但是子類不寫父類的屬性和方法,這時候也是可以。而如果子類要寫與父類的成員變量相同,這時在子類中是需要訪問父類非私有成員變量時,必須寫super關鍵字,用法就是super.父類成員變量名,即可。
  • Python3 pickle模塊的使用詳解
    pickle模塊特點1、只能在python中使用,只支持python的基本數據類型。2、可以處理複雜的序列化語法。
  • python他律筆記系列二
    語法if語句  if-elif-else 與c/c++ 不同的是每個條件後面需要使用冒號:另外python中無switch-case語句while語句 與c/c++一致所需要注意的就是需要加上冒號:另外python中無do...while循環對於 while 1這類死循環可以通過crtl+c強制退出但是有while...else 語句for語句