Python中沒有存取控制的關鍵字,例如private、protected (java)等等。但是,在Python編碼中,有一些約定來進行存取控制。
1、單底線”_” 在Python中,通過單底線”“來實現模組層級別的私人化,變數除外。一般約定以單底線”“開頭的函數為模組私人的,也就是說”from moduleName import * “將不會引入以單底線”_”開頭的函數。
現在有一個模組 example_example.py,內容用如下,模組中一個變數名和一個函數名分別以”_”開頭:
name = 'bruce'_tall = 180def call_for(): print('name is :',name) print('_tall is',_tall)def _call_for(): print('name is :',name)#_call_for = _call_for() print('_tall is',_tall)
_tall is 180
再次調用該指令碼:
#呼叫指令碼模組example_exampleimport example_example#調用不帶底線變數example_example.nameOut[12]: 'bruce'#調用帶底線變數example_example._tall #對於變數單底線不會影響調用Out[13]: 180#調用不帶底線函數example_example.call_for()Out[16]: name is : bruce _tall is 180#調用不帶底線函數會報錯example_example._call_for()Traceback (most recent call last): File "<ipython-input-15-e642b1386946>", line 1, in <module> example_example._call_for()TypeError: 'NoneType' object is not callable
2、雙底線”__” 對於Python中的類屬性,可以通過雙底線”__”來實現一定程度的私人化,因為雙底線開頭的屬性在運行時會被”混淆”(mangling)。
class person(object): tall = 180 hobbies = [] def __init__(self, name, age,weight): self.name = name self.age = age self.weight = weight self.__Id = 430 @classmethod def infoma(cls): print(cls.__Id)# person.infoma()Bruce = person("Bruce", 25,180)print(Bruce.age)print(Bruce.__Id)
25
---------------------------------------------------------------------------AttributeError Traceback (most recent call last)<ipython-input-32-ae0c1d7abe5a> in <module>() 15 Bruce = person("Bruce", 25,180) 16 print(Bruce.age)---> 17 print(Bruce.__Id)AttributeError: 'person' object has no attribute '__Id'
其實,通過內建函數dir()就可以看到其中的一些原由,”__address”屬性在運行時,屬性名稱被改為了”_person__address”(屬性名稱前增加了單底線和類名)
print(dir(Bruce))
#可以看到Bruce中有_person__Id的屬性,相較原__Id屬性,變得可調用['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_person__Id', 'age', 'hobbies', 'infoma', 'name', 'tall', 'weight']
所以說,即使是雙底線,也沒有實現屬性的私人化,因為通過下面的方式還是可以直接存取”__address”屬性:
print(Bruce._person__Id)
430 #通過使屬性__Id名前增加了單底線_和類名person來實現屬性的可調用
雙底線的另一個重要的目地是,避免子類對父類同名屬性的衝突
class A(object): def __init__(self): self.__private() self.public() def __private(self): print('A.__private()') def public(self): print('A.public()')class B(A): def __private(self): print('B.__private()') def public(self): print('B.public()')b = B()
A.__private()
B.public()
當執行個體化B的時候,由於沒有定義_ _init_ 函數,將調用父類的 _ _init_ _,但是由於雙底線的”混淆”效果,”self.__private()”將變成 “self._A__private()”。
總結:
“_”和” __”的使用 更多的是一種規範/約定,不沒有真正達到限制的目的: “_”:以單底線開頭的表示的是 protected 類型的變數,即只能允許其本身與子類進行訪問;同時表示弱內部變數標示,如,當使用”from moduleNmae import *”時,不會將以一個底線開頭的對象引入。 “__”:雙底線的表示的是私人類型的變數。只能是允許這個類本身進行訪問了,連子類也不可以,這類屬性在運行時屬性名稱會加上單底線和類名。