python super()(轉載)

一、問題的發現與提出

  在Python類的方法(method)中,要調用父類的某個方法,在Python 2.2以前,通常的寫法如代碼段1:

?代碼段1:

class A:def __init__(self):print "enter A"print "leave A"class B(A):def __init__(self):print "enter B"A.__init__(self)print "leave B">>> b = B()enter Benter Aleave Aleave B

?

即,使用非綁定的類方法(用類名來引用的方法),并在參數列表中,引入待綁定的對象(self),從而達到調用父類的目的。

  這樣做的缺點是,當一個子類的父類發生變化時(如類B的父類由A變為C時),必須遍歷整個類定義,把所有的通過非綁定的方法的類名全部替換過來,例如代碼段2,

?代碼段2:

class B(C):    # A --> Cdef __init__(self):print "enter B"C.__init__(self) # A --> Cprint "leave B"

  如果代碼簡單,這樣的改動或許還可以接受。但如果代碼量龐大,這樣的修改可能是災難性的。

  因此,自Python 2.2開始,Python添加了一個關鍵字super,來解決這個問題。下面是Python 2.3的官方文檔說明:

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

??Return the superclass of type. If the second argument is omitted the super object
??returned is unbound. If the second argument is an object, isinstance(obj, type)?
??must be true. If the second argument is a type, issubclass(type2, type) must be?
??true. super() only works for new-style classes.

??A typical use for calling a cooperative superclass method is:

???class C(B):
?????? def meth(self, arg):
?????????? super(C, self).meth(arg)

??New in version 2.2.

  從說明來看,可以把類B改寫如代碼段3:

?代碼段3:

class A(object):    # A must be new-style classdef __init__(self):print "enter A"print "leave A"class B(C):     # A --> Cdef __init__(self):print "enter B"super(B, self).__init__()print "leave B"

  嘗試執行上面同樣的代碼,結果一致,但修改的代碼只有一處,把代碼的維護量降到最低,是一個不錯的用法。因此在我們的開發過程中,super關鍵字被大量使用,而且一直表現良好。

  在我們的印象中,對于super(B, self).__init__()是這樣理解的:super(B, self)首先找到B的父類(就是類A),然后把類B的對象self轉換為類A的對象(通過某種方式,一直沒有考究是什么方式,慚愧),然后“被轉換”的類A對象調用自己的__init__函數。考慮到super中只有指明子類的機制,因此,在多繼承的類定義中,通常我們保留使用類似代碼段1的方法。

  有一天某同事設計了一個相對復雜的類體系結構(我們先不要管這個類體系設計得是否合理,僅把這個例子作為一個題目來研究就好),代碼如代碼段4:

?代碼段4:

 1 class A(object):
 2   def __init__(self):
 3    print "enter A"
 4    print "leave A"
 5 
 6  class B(object):
 7   def __init__(self):
 8    print "enter B"
 9    print "leave B"
10 
11  class C(A):
12   def __init__(self):
13    print "enter C"
14    super(C, self).__init__()
15    print "leave C"
16 
17  class D(A):
18   def __init__(self):
19    print "enter D"
20    super(D, self).__init__()
21    print "leave D"
22  class E(B, C):
23   def __init__(self):
24    print "enter E"
25    B.__init__(self)
26    C.__init__(self)
27    print "leave E"
28 
29  class F(E, D):
30   def __init__(self):
31    print "enter F"
32    E.__init__(self)
33    D.__init__(self)
34    print "leave F"

?

??f = F() result:

enter Fenter Eenter Bleave Benter Center Denter Aleave Aleave Dleave Cleave Eenter Denter Aleave Aleave Dleave F

復制代碼
 enter Fenter Eenter Bleave Benter Center Denter Aleave Aleave Dleave Cleave Eenter Denter Aleave Aleave Dleave F
復制代碼

  明顯地,類A和類D的初始化函數被重復調用了2次,這并不是我們所期望的結果!我們所期望的結果是最多只有類A的初始化函數被調用2次——其實這是多繼承的類體系必須面對的問題。我們把代碼段4的類體系畫出來,如下圖:

??? object
?? |?????? \
?? |??????? A
?? |????? / |
?? B??C? D
??? \?? /?? |
????? E??? |
??????? \?? |
????????? F

  按我們對super的理解,從圖中可以看出,在調用類C的初始化函數時,應該是調用類A的初始化函數,但事實上卻調用了類D的初始化函數。好一個詭異的問題!

  也就是說,mro中記錄了一個類的所有基類的類類型序列。查看mro的記錄,發覺包含7個元素,7個類名分別為:

?F E B C D A object

  從而說明了為什么在C.__init__中使用super(C, self).__init__()會調用類D的初始化函數了。 ???

  我們把代碼段4改寫為:

?代碼段9:

class A(object):def __init__(self):print "enter A"super(A, self).__init__()  # newprint "leave A"class B(object):def __init__(self):print "enter B"super(B, self).__init__()  # newprint "leave B"class C(A):def __init__(self):print "enter C"super(C, self).__init__()print "leave C"class D(A):def __init__(self):print "enter D"super(D, self).__init__()print "leave D"class E(B, C):def __init__(self):print "enter E"super(E, self).__init__()  # changeprint "leave E"class F(E, D):def __init__(self):print "enter F"super(F, self).__init__()  # changeprint "leave F"

復制代碼
class A(object):def __init__(self):print "enter A"super(A, self).__init__()  # newprint "leave A"class B(object):def __init__(self):print "enter B"super(B, self).__init__()  # newprint "leave B"class C(A):def __init__(self):print "enter C"super(C, self).__init__()print "leave C"class D(A):def __init__(self):print "enter D"super(D, self).__init__()print "leave D"class E(B, C):def __init__(self):print "enter E"super(E, self).__init__()  # changeprint "leave E"class F(E, D):def __init__(self):print "enter F"super(F, self).__init__()  # changeprint "leave F"
復制代碼

f = F() result:

 enter Fenter Eenter Benter Center Denter Aleave Aleave Dleave Cleave Bleave Eleave F

  明顯地,F的初始化不僅完成了所有的父類的調用,而且保證了每一個父類的初始化函數只調用一次。

  再看類結構:

    object/   \/      A|     /   \B-1  C-2   D-2\   /    /E-1    /\  /F

E-1,D-2是F的父類,其中表示E類在前,即F(E,D)。

所以初始化順序可以從類結構圖來看出 : F->E->B -->C --> D --> A

由于C,D有同一個父類,因此會先初始化D再是A。

三、延續的討論

  我們再重新看上面的類體系圖,如果把每一個類看作圖的一個節點,每一個從子類到父類的直接繼承關系看作一條有向邊,那么該體系圖將變為一個有向圖。不能發現mro的順序正好是該有向圖的一個拓撲排序序列。

  從而,我們得到了另一個結果——Python是如何去處理多繼承。支持多繼承的傳統的面向對象程序語言(如C++)是通過虛擬繼承的方式去實現多繼承中父類的構造函數被多次調用的問題,而Python則通過mro的方式去處理。

  但這給我們一個難題:對于提供類體系的編寫者來說,他不知道使用者會怎么使用他的類體系,也就是說,不正確的后續類,可能會導致原有類體系的錯誤,而且這樣的錯誤非常隱蔽的,也難于發現。

四、小結

  1. super并不是一個函數,是一個類名,形如super(B, self)事實上調用了super類的初始化函數,
?????? 產生了一個super對象;
  2. super類的初始化函數并沒有做什么特殊的操作,只是簡單記錄了類類型和具體實例;
  3. super(B, self).func的調用并不是用于調用當前類的父類的func函數;
  4. Python的多繼承類是通過mro的方式來保證各個父類的函數被逐一調用,而且保證每個父類函數
?????? 只調用一次(如果每個類都使用super);
  5. 混用super類和非綁定的函數是一個危險行為,這可能導致應該調用的父類函數沒有調用或者一
?????? 個父類函數被調用多次。

轉載于:https://www.cnblogs.com/xiaoerlang/p/3460939.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/259080.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/259080.shtml
英文地址,請注明出處:http://en.pswp.cn/news/259080.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Swagger+Spring mvc生成Restful接口文檔

2019獨角獸企業重金招聘Python工程師標準>>> Swagger 是一個規范和完整的框架,用于生成、描述、調用和可視化 RESTful 風格的 Web 服務。總體目標是使客戶端和文件系統作為服務器以同樣的速度來更新。文件的方法,參數和模型緊密集成到服務器端…

JavaScript——變量與基本數據類型

前言 JavaScript中的變量為松散類型,所謂松散類型就是指當一個變量被申明出來就可以保存任意類型的值,就是不像SQL一樣申明某個鍵值為int就只能保存整型數值,申明varchar只能保存字符串。一個變量所保存值的類型也可以改變,這在Ja…

vscode可以打開jupyternotebook嗎_剛剛,官方宣布 VS Code 支持 Python 全開發了!

關注Python高校每天早上23:10準時推送北京時間 2019 年 9 月 21 日,PyCon China 2019 在上海舉行。在下午的演講中,來自微軟開發工具事業部的資深研發工程師韓駿做了主題為《Python 與 Visual Studio Code 在人工智能應用中的最佳 Azure 實踐》的演講。在…

C++類的內聯成員函數應放在哪

今天復習C Primer的時候,看到了關于C類的內聯成員函數的放置,應該放在頭文件中。那么這到底是為什么 呢?僅僅是一種代碼規范問題還是必須這樣做呢? 下面我就來講講我自己的理解吧。要徹底理解這個問題,首先就要了解下函…

python selenium自動化(三)Chrome Webdriver的兼容

當一個自動化測試被實現在一個瀏覽器之后,我們會希望我們的測試能夠覆蓋到盡量多的別的瀏覽器。通過跨平臺的測試來保證我們的程序在多個瀏覽器下都能正常工作。 在安裝了selenium之后,firefox webdriver和IE webdriver就已經是ready to use的了&#xf…

NDK 編譯armebai-v7a的非4字節對齊crash Fatal signal 7 (SIGSEGV) 錯誤解決

一直都是編譯armabi的。沒有不論什么問題,這個架構是軟件模擬浮點運算的。后來看到NDK文檔上說armabi-v7a是針對有硬件處理浮點計算的arm cpu的。 于是就改動配置編譯armebai-v7a的so文件。 結果是編譯沒問題。一執行就是crash掉,Fatal signal 7 (SIGSEG…

作業三

作業三 第一章問題:書上寫的“Bug的多少可以直接衡量一個軟件的開發效率、用戶滿意度、可靠性和可維護性”,那么一個比較完好的軟件中一般大概會出現多少Bug? 第二章問題:現在開始訓練寫更多的程序能否更早地達到軟件工程師的標準&#xff1…

springboot默認數據源如何設置連接數_Spring Boot系列之配置數據庫連接池

在實際的應用開發中,與數據庫交互通常使用數據庫連接池來重用Connection對象,減少資源消耗。Spring Boot 的數據源是自動配置的。在 Spring Boot 2.2.1 版本中,有幾種數據源配置可選,它們按照 HikariCP -> Tomcat -> DBCP2 …

使用Qt正則表達式提取全路徑的文件名

問題描述: 給定三個全路徑,例如 path1"C:/Users/asus/Desktop/nefertiti_4465.obj"; path2"C:/Users/asus/Desktop/nefertiti_4465_k1.txt"; path3"C:/Users/asus/Desktop/nefertiti_4465_k2.txt"; 我希望說明path2和pa…

Beyond Compare 3.3.8 build 16340 + Key

本文摘錄自冰點社區:http://forum.z27315.com/topic/14746-beyond-compare-338-build-16340-key/ Download Beyond Compare 3 Current Version: 3.3.8, build 16340, released June 19, 2013 Windows 版本 Windows Standard and Pro EditionsEnglish version 5800k…

hdu 1198 Farm Irrigation

題目鏈接: http://acm.hdu.edu.cn/showproblem.php?pid1198 題目大意: 有一大塊土地需要澆水,這塊土地由很多的小塊土地(有十一種)組成,小塊土地上有水溝,問至少需要建幾個井,才能灌…

strcpy_s、sptintf_s與strcat_s的使用

strcpy_s、sptintf_s與strcat_s是strcpy、sptintf與strcat的安全版本,均是通過指定緩沖區長度來避免存在的溢出風險。 strcpy_s 與strcpy strcpy_s和strcpy函數的功能幾乎是一樣的。strcpy函數,就象gets函數一樣,它沒有方法來保證有效的緩沖…

小米一鍵上鎖工具_小米首款高端全自動智能鎖火熱預售中,一觸開啟全自動時代...

近些年,隨著科技的發展,人工智能逐漸走入大眾視野。人類社會也正從信息時代向“智能時代”過渡,在整個過程中智能家居領域的蓬勃發展可謂當仁不讓,一直備受用戶矚目。智能鎖作為家的第一道守護防線,家庭物聯網入口的關…

Eigen+suitesparse for windows 安裝

Eigen是著名的C矩陣運算庫,提供了許多矩陣運算的接口,主要包括兩大部分,一部分是稠密矩陣,另一部分是稀疏矩陣。Eigen以源碼形式提供給大家,用的時候,只要將源碼包含在項目的包含路徑上,具體安裝…

軟件盤控制的問題

2019獨角獸企業重金招聘Python工程師標準>>> 在全屏模式或者是沉寢室標題欄 方案一:全屏模式 1.軟鍵盤被EditText遮擋住了,如果說EditText被嵌套在有滑動的視圖中,采取的方式是: activity中設置此屬性 android:windowSoftInputMode"…

python語言學習零基礎教學視頻_Python告白小白視頻教程(零基礎入門)

1 Python編程基礎入門篇通過本次課程的學習,我們每個人都可以進入python世界里,從簡單到高級,讓人人都能學會python,我們在學習的時候,python讓我們的運維變得更有樂趣,讓我們的運維更加的高大上&#xff0…

SQL 快速入門2.1

MySQL top(MySQL limit)語法 SELECT column_name(s) FROM table_name LIMIT number 例子 SELECT * FROM Persons LIMIT 5 SQL LIKE 操作符 SQL LIKE 操作符語法 SELECT column_name(s) FROM table_name WHERE column_name LIKE pattern 原始的表 (用在例…

sencha touch 入門系列 (一)sencha touch 簡介

參考鏈接:http://mobile.51cto.com/others-278381.htm Sencha touch 是基于JavaScript編寫的Ajax框架ExtJS,將現有的ExtJS整合JQTouch、Raphaël庫,推出適用于最前沿Touch Web的移動應用開發框架,該框架是世界上第一個 基于HTML5的Mobile App框架…

求二叉樹的深度和寬度

// 求二叉樹的深度和寬度.cpp : 定義控制臺應用程序的入口點。 <pre name"code" class"cpp">#include <iostream> #include <queue> using namespace std;struct BTNode {char m_value;BTNode *m_left;BTNode *m_right; };//先序創建二叉…

漢堡包

在我們結對的這些天里&#xff0c;我清晰的感受到同伴對我的幫助&#xff0c;每當我有不懂的時候她都會積極的幫助我&#xff0c;也會聽取我的意見積極配合我&#xff0c;在我懶惰的時候也能夠提醒督促我&#xff0c;我想這些只有結對時才能體會到。我們都知道&#xff0c;結對…