關(guān)于python的繼承的一個疑問
問題描述

如圖所示,B 類繼承了 A 類;
當實例化對象時,B 類中沒有構(gòu)造函數(shù), 應(yīng)該調(diào)用父類的構(gòu)造函數(shù) __init__
但是里邊的 self.__pirv() 為啥調(diào)用到父類 A 的 __priv, 而 self.pub() 又調(diào)到 B 中的 pub
求解?
問題解答
回答1:在Python從__開始的方法名稱不是private,__的意思就讓Python做name mangling,name mangling的結(jié)果就是_A__priv。這樣的方法你應(yīng)該不要overwrite。如果你想讓子類overwrite一個方法你只用_,不用__
回答2:想了一下這個問題,我是這樣理解的:
class A(object): def __init__(self):self.__priv() # _A__priv()self.pub() def __priv(self):print('private A') def pub(self):print('public A')class B(A): def __init__(self):self.__priv() # 調(diào)用_B__priv()對比super(B, self).__init__() # 在這里顯式調(diào)用父類的`__init__()`方法 def __priv(self):print('private B') def pub(self):print('public B')if __name__ == ’__main__’: b = B()
在子類的實例調(diào)用__init__()方法時,從子類B本身中查找pub()方法,顯然是存在的,因此會調(diào)用B類自身的pub()方法;然而在調(diào)用__priv()方法時,由于Python對私有成員進行了名稱改編,你實際調(diào)用的是_A__priv()方法,而B類中并沒有這個方法,有的只有_B__priv(),因此調(diào)用父類A中的_A__priv(),于是產(chǎn)生了這樣的結(jié)果。這是我個人的理解,如果有誤歡迎指正,謝謝。
回答3:關(guān)於問題本身, @Xavier 和 @Christoph 已經(jīng)有詳細的說明了
如果你還不明白可以試著這樣做:
原本的代碼:
class A: def __init__(self):self.__priv() # 等等改成 self._A__priv()self.public() def __priv(self): # 等等改成 def _A__priv(self):print(’private of A’) def public(self):print(’public of A’)class B(A): def __priv(self): # 等等改成 self._B__priv(self):print(’private of B’) def public(self):print(’public of B’)b = B()
自行手動進行 name mangling:
class A: def __init__(self):self._A__priv()self.public() def _A__priv(self):print(’private of A’) def public(self):print(’public of A’)class B(A): def _B__priv(self):print(’private of B’) def public(self):print(’public of B’)b = B()
B 在這裡繼承了 A 的所有屬性包含:
__init__
_A__priv
public
而 B 自己定義了:
_B__priv
public (此處覆寫了 A 的 public)
所以最後你會看到 dir(b) 裡面有:
__init__ (從 A 繼承的)
_A__priv (從 A 繼承的)
_B__priv (自己定義的)
public (自己定義的)
最後當 __init__ 被呼叫時, 會調(diào)用 _A__priv, 而 B 裡面的確有這個方法
囉唆補充一下, Python 本身並沒有真正的 private 機制, 因為了解 name mangling 的人就能對以雙底線開頭的屬性做存取, 比如說我可以很輕易地寫出:
a = A()a._A__priv() # 防都防不住
簡單來說這個機制是個:
防呆 的機制, 不是個 防小人 的機制,
防止意外 存取的機制, 不是個防止 刻意存取 的機制
但是這個機制並非所有人都覺得好(個人就不喜歡, 使用雙底線開頭命名既麻煩也沒太多實際的幫助), 所以你可以在很多的 python 代碼中發(fā)現(xiàn): 大家比較常使用以 單個底線 開頭的保護方式, 這種做法是個公認的慣例(註1), 對於稍有經(jīng)驗的程序員來說足以防呆, 且不會有任何額外的效果和意外的狀況發(fā)生
Ian Bicking 有一段話是這樣說的 (Ian Bicking 是 Python 大神, 這段話我是在 Luciano Ramalho 的的 Fluent Python 中看到的):
永遠不要在前面使用兩個底線. 這是很讓人生氣的自私行為, 如果你不希望造成名稱衝突(註2), 可以明確地重整名稱(例如: _MyThing_blahblah). 實質(zhì)上這與使用雙底線是同一件事情, 不過他是公開的, 雙底線是私下的行為.
所以我的建議是, 使用 _priv 會是更好的選擇.
註1: 以單底線開頭的屬性不會具有任何特殊的性質(zhì), 他僅僅是依靠 Python 程序員的共識而產(chǎn)生的具有象徵意義的符號性手法, 就好像有些語言會使用 const 來標明常量, 而我們也可以僅依賴 常量使用大寫命名 的共識來避免意外的狀況發(fā)生
註2: 之所以想要以 private 性質(zhì)來保護屬性, 最常見的就是因為名稱衝突引起的意外存取
我回答過的問題: Python-QA
相關(guān)文章:
1. phpstudy8.1沒集成mysql-front2. 關(guān)docker hub上有些鏡像的tag被標記““This image has vulnerabilities””3. node.js - mongodb查找子對象的名稱為某個值的對象的方法4. html5 - datatables 加載不出來數(shù)據(jù)。5. Docker for Mac 創(chuàng)建的dnsmasq容器連不上/不工作的問題6. docker鏡像push報錯7. javascript - 在 model里定義的 引用表模型時,model為undefined。8. javascript - QQ第三方登錄的問題9. 前端 - @media query 使用出現(xiàn)的問題?10. docker 下面創(chuàng)建的IMAGE 他們的 ID 一樣?這個是怎么回事????

網(wǎng)公網(wǎng)安備