在Class内部,可以有屬性和方法,而外部代碼可以通過直接調用執行個體變量的方法來操作資料,這樣,就隐藏了内部的複雜邏輯。但是,從前面Student類的定義來看,外部代碼還是可以自由地修改一個執行個體的
name
、
score
屬性:
>>> bart = Student('Bart Simpson', 59)
>>> bart.score
59
>>> bart.score = 99
>>> bart.score
99
如果要讓内部屬性不被外部通路,可以把屬性的名稱前面加上兩個下劃線
__
, 在Python中,執行個體的變量名如果以
__
開頭,就變成了一個私有變量(private),隻有内部可以通路,外部不能通路,是以,我們把Student類改一改:
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
改完後,對于外部代碼來說,沒有什麼改動,但是已經無法從外部通路
執行個體變量.__name
和
執行個體變量.__score
了:
>>> bart = Student('Bart Simpson', 59)
>>> bart.__name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'
這樣就確定了外部代碼不能随意修改對象内部的狀态,這樣通過通路限制的保護,代碼更加健壯。
但是如果外部代碼要擷取name 和score怎麼辦?可以給Student類增加
get_name
和
get_score
這樣的方法:
class Student(object):
...
def get_name(self):
return self.__name
def get_score(self):
return self.__score
如果又要允許外部代碼修改score怎麼辦?可以再給Student類增加
set_score
方法:
class Student(object):
...
def set_score(self, score):
self.__score = score
你也許會問,原先那種直接通過
bart.score = 99
也可以修改啊,為什麼要定義一個方法大費周折?因為在方法中,可以對參數做檢查,避免傳入無效的參數:
class Student(object):
...
def set_score(self, score):
if 0 <= score <= 100:
self.__score = score
else:
raise ValueError('bad score')
需要注意的是,在Python中,變量名類似
__XXX__
的,也就是以雙下劃線開頭,并且以雙下劃線結尾的,是特殊變量,特殊變量是可以直接通路的,不是private變量,是以,不能用
__name__
、
__score__
這樣的變量名。
有些時候,你會看到以一個下劃線開頭的執行個體變量名,比如:
_name
,這樣的執行個體變量外部是可以通路的,但是,按照約定俗成的規定,當你看到這樣的變量時,意思就是,“雖然我可以被這樣通路,但是,請把我視為私有變量,不要随意通路”。
雙下劃線開頭的執行個體變量是不是一定不能從外部通路呢?其實也不是,不能直接通路
__name
是因為Python解釋器對外把
__name
變量改成了
_Student__name
,是以,仍然可以通過
_Student__name
來通路
__name
變量:
>>> bart._Student__name
'Bart Simpson'
但是強烈建議你不要這麼幹,因為不同版本的Python解釋器可能會把
__name
改成不同的變量名。
總的來說就是,Python本身沒有任何機制阻止你幹壞事一切全靠自覺。
最後注意下面這種錯誤寫法:
>>> bart = Student('Bart Simpson', 59 )
>>> bart.get_name()
'Bart Simpson'
>>> bart.__name = 'New Name'
>>> bart.__name
'New Name'
表面上看,外部代碼“成功”地設定了
__name
變量,但實際上
__name
變量和class内部的
__name
變量不是一個變量!内部的
__name
變量已經被Python解釋器自動改成了
_Student__name
,而外部代碼給
bart
新增了一個
__name
變量。
>>> bart.get_name() # get_name()内部傳回self.__name
'Bart Simpson'