8.5 在類中封裝屬性名

2018-02-24 15:26 更新

問題

你想封裝類的實例上面的“私有”數(shù)據(jù),但是Python語言并沒有訪問控制。

解決方案

Python程序員不去依賴語言特性去封裝數(shù)據(jù),而是通過遵循一定的屬性和方法命名規(guī)約來達到這個效果。第一個約定是任何以單下劃線_開頭的名字都應該是內(nèi)部實現(xiàn)。比如:

class A:
    def __init__(self):
        self._internal = 0 # An internal attribute
        self.public = 1 # A public attribute

    def public_method(self):
        '''
        A public method
        '''
        pass

    def _internal_method(self):
        pass

Python并不會真的阻止別人訪問內(nèi)部名稱。但是如果你這么做肯定是不好的,可能會導致脆弱的代碼。同時還要注意到,使用下劃線開頭的約定同樣適用于模塊名和模塊級別函數(shù)。例如,如果你看到某個模塊名以單下劃線開頭(比如_socket),那它就是內(nèi)部實現(xiàn)。類似的,模塊級別函數(shù)比如 sys._getframe() 在使用的時候就得加倍小心了。

你還可能會遇到在類定義中使用兩個下劃線(__)開頭的命名。比如:

class B:
    def __init__(self):
        self.__private = 0

    def __private_method(self):
        pass

    def public_method(self):
        pass
        self.__private_method()

使用雙下劃線開始會導致訪問名稱變成其他形式。比如,在前面的類B中,私有屬性會被分別重命名為 _B__private_B__private_method 。這時候你可能會問這樣重命名的目的是什么,答案就是繼承——這種屬性通過繼承是無法被覆蓋的。比如:

class C(B):
    def __init__(self):
        super().__init__()
        self.__private = 1 # Does not override B.__private

    # Does not override B.__private_method()
    def __private_method(self):
        pass

這里,私有名稱 __private__private_method被重命名為 _C__private_C__private_method ,這個跟父類B中的名稱是完全不同的。

討論

上面提到有兩種不同的編碼約定(單下劃線和雙下劃線)來命名私有屬性,那么問題就來了:到底哪種方式好呢?大多數(shù)而言,你應該讓你的非公共名稱以單下劃線開頭。但是,如果你清楚你的代碼會涉及到子類,并且有些內(nèi)部屬性應該在子類中隱藏起來,那么才考慮使用雙下劃線方案。

還有一點要注意的是,有時候你定義的一個變量和某個保留關鍵字沖突,這時候可以使用單下劃線作為后綴,例如:

lambda_ = 2.0 # Trailing _ to avoid clash with lambda keyword

這里我們并不使用單下劃線前綴的原因是它避免誤解它的使用初衷(如使用單下劃線前綴的目的是為了防止命名沖突而不是指明這個屬性是私有的)。通過使用單下劃線后綴可以解決這個問題。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號