由Python的super()函數想到的

来源:http://www.cnblogs.com/john-j/archive/2016/07/10/5658449.html
-Advertisement-
Play Games

python-super 由Python的super()函數想到的 首先看一下super()函數的定義: 返回一個代理對象, 這個對象負責將方法調用分配給第一個參數的一個父類或者同輩的類去完成. parent or sibling class 如何確定? 第一個參數的__mro__屬性決定了搜索的順 ...


python-super

由Python的super()函數想到的

首先看一下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:
    pass

class A(Root):
    pass

class B(Root):
    pass

class C(A, B):
    pass

print(C.__mro__)

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

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

調用super()這個構造方法時, 只是返回一個super()對象, 並不做其他的操作.
然後對這個super對象進行方法調用時, 發生的事情如下:

  1. 找到第一個參數的__mro__列表中的下一個直接定義了該方法的類, 並實例化出一個對象
  2. 然後將這個對象的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), 這裡的selfsuper()的第二個參數, 是編譯器自動填充的, 也就是A__init__的第一個參數, 這樣就完成對__init__方法調用的分配.

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

參數說明

super() -> same as super(__class__, <first argument>) # <first argument>指的是調用super的函數的第一個參數
super(type) -> unbound super object
super(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()的兩種常見用法:

  1. 單繼承中, super用來指代隱式指代父類, 避免直接使用父類的名字
  2. 多繼承中, 解決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沒辦法隱式傳遞, 所以必須得顯式地傳遞.

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 根據日誌,確實發生了FullGC,計算資源被耗光 ...
  • 1、拆分消息隊列 原本在登錄註冊的時候需要使用到簡訊發送,這個需要使用到消息隊列,當時只放入在項目中 現在的新需求在各個環節都有不同的消息推送,短息服務,以及日誌保存,這些索性單獨拎出來作為一個服務提供 (消息隊列採用RabbitMQ,各位看管有興趣可以參考之前發的文章,另外MQ也有ActiveMQ ...
  • 一、MVC概要 MVC是模型(Model)、視圖(View)、控制器(Controller)的簡寫,是一種軟體設計規範,用一種將業務邏輯、數據、顯示分離的方法組織代碼,MVC主要作用是降低了視圖與業務邏輯間的雙向偶合。MVC不是一種設計模式,MVC是一種架構模式。當然不同的MVC存在差異。 在web ...
  • 本節進一步介紹對異常的處理,finally的詭異之處,checked/unchecked exception的區別,異常的來源,以及處理的思維邏輯 ... ...
  • 介紹了sparklyr在Linux下的安裝和使用,包括如何解決遇到的問題,如何部署本地spark和集群spark,並給出了官方示例! ...
  • 迴圈結構 <!--EndFragment--> <!--EndFragment--> 【寫在開頭:】 『 生活中的迴圈: C語言中的迴圈: 迴圈結構是程式中一種很重要的結構。其特點是,在給定的條件成立時,反覆執行某程式段,直到條件不成立為止。 C語言中提供了多種迴圈語句: 1)goto語句和if構成 ...
  • 在Qt中,如何響應動作。這會用到Qt的信號和槽機制。 我的理解:它和Win32程式的消息響應機制差不多吧。 信號,簡單理解就是:當我們點擊一個按鈕時,這個按鈕自身就會產生一個叫作"單擊"的信息,這個信息說明瞭剛剛我們點擊了這一個按鈕。產生的這個信息就相當於自己發射了一個信號,表明一個用戶動作已經發生 ...
  • R語言在Linux下安裝一不小心就容易出錯,本文給出了Ubuntu 16.04LTS版本下的R和RStudio Server的安裝方法,不需要自己下載相關包,方便,快捷! ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...