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__被呼叫時, 會調用_A__priv, 而B裡面的確有這個方法
囉唆補充一下, Python 本身並沒有真正的 private 機制, 因為了解 name mangling 的人就能對以雙底線開頭的屬性做存取, 比如說我可以很輕易地寫出:
在Python从__开始的方法名称不是private,__的意思就让Python做name mangling,name mangling的结果就是_A__priv。这样的方法你应该不要overwrite。如果你想让子类overwrite一个方法你只用_,不用__
想了一下这个问题,我是这样理解的:
在子类的实例调用
__init__()
方法时,从子类B
本身中查找pub()
方法,显然是存在的,因此会调用B
类自身的pub()
方法;然而在调用__priv()
方法时,由于Python对私有成员进行了名称改编,你实际调用的是_A__priv()
方法,而B
类中并没有这个方法,有的只有_B__priv()
,因此调用父类A
中的_A__priv()
,于是产生了这样的结果。这是我个人的理解,如果有误欢迎指正,谢谢。關於問題本身, @Xavier 和 @Christoph 已經有詳細的說明了
如果你還不明白可以試著這樣做:
原本的代碼:
自行手動進行 name mangling:
B
在這裡繼承了A
的所有屬性包含:__init__
_A__priv
public
而
B
自己定義了:_B__priv
public
(此處覆寫了A
的public
)所以最後你會看到
dir(b)
裡面有:__init__
(從A
繼承的)_A__priv
(從A
繼承的)_B__priv
(自己定義的)public
(自己定義的)最後當
__init__
被呼叫時, 會調用_A__priv
, 而B
裡面的確有這個方法囉唆補充一下, Python 本身並沒有真正的 private 機制, 因為了解 name mangling 的人就能對以雙底線開頭的屬性做存取, 比如說我可以很輕易地寫出:
簡單來說這個機制是個:
防呆的機制, 不是個防小人的機制,
防止意外存取的機制, 不是個防止刻意存取的機制
但是這個機制並非所有人都覺得好(個人就不喜歡, 使用雙底線開頭命名既麻煩也沒太多實際的幫助), 所以你可以在很多的 python 代碼中發現: 大家比較常使用以單個底線開頭的保護方式, 這種做法是個公認的慣例(註1), 對於稍有經驗的程序員來說足以防呆, 且不會有任何額外的效果和意外的狀況發生
Ian Bicking 有一段話是這樣說的 (Ian Bicking 是 Python 大神, 這段話我是在 Luciano Ramalho 的的 Fluent Python 中看到的):
所以我的建議是, 使用
_priv
會是更好的選擇.註1: 以單底線開頭的屬性不會具有任何特殊的性質, 他僅僅是依靠 Python 程序員的共識而產生的具有象徵意義的符號性手法, 就好像有些語言會使用
const
來標明常量, 而我們也可以僅依賴常量使用大寫命名的共識來避免意外的狀況發生註2: 之所以想要以 private 性質來保護屬性, 最常見的就是因為名稱衝突引起的意外存取
我回答過的問題: Python-QA