每日一python(11):python中下劃線的意義
文章目錄
1 單下劃線(_)
1.1 在直譯器中
單下劃線(_)符號是指互動直譯器中最後一次執行語句的返回結果。這種用法最初出現在CPython直譯器中,其他直譯器後來也都跟進了。
例如:
>>> _
Traceback (most recent call last):
File "", line 1, in
NameError: name '_' is not defined
>>> 42
>>> _
42
>>> 'alright!' if _ else ':('
'alright!'
>>> _
'alright!'
1.2 作為名稱使用
這與上面一點稍微有些聯絡,此時的單下劃線作為臨時性的名稱使用。這樣,當其他人閱讀你的程式碼時將會知道,你分配了一個特定的名稱,但是並不會在後面再次用到該名稱。
例如,下面的例子中,你可能對迴圈計數中的實際值並不感興趣,此時就可以使用(_)。
n = 42
for _ in range(n):
do_something()
2 變數中的下劃線
2.1 單下劃線開頭的變數(_XXX)
以單下劃線開頭的變數,表明這是一個**受保護(protected)**的變數,原則上不允許直接訪問,但是外部類還是可以訪問到這個變數。因為這只是一個程式設計師之間的約定,用於警告說明這是一個受保護的變數,外部類不要去訪問它。
以單下劃線做字首的變量表名了這個變數是“私有的”。在 有些 匯入( import *) 的場景中,下一個使用你程式碼的人(或者你本人)會明白這個名稱僅內部使用。Python documentation裡面寫道:
以單下劃線’_'為字首的名稱,如_xxx,應該被視為API中非公開的部分(不管是函式,方法還是資料成員)。此時,應該將它們看做一種實現細節,在修改他們時無需對外部通知。
例如:
class Student(object):
def __init__(self, name, age):
self._name = name
self.age = age
st = Student("Yi", 31)
print(st._name) # Yi
print(st.age) # 31
正如上面所說,這確實類似一種慣例,因為它對直譯器來說確實有一定的意義,如果你寫了程式碼 from <模組/包名> import *
,那麼以’_'開頭的名稱都不會被匯入,除非模組或包中的__all__
列表顯示地包含了它們。不過值得注意的是,如果使用 import module
這樣的方式匯入模組,仍然可以用 module._var
這樣的形式訪問到這樣的物件。
例如:
'''模組test1.py'''
# 定義2個模組變數
num = 10
_num = 40
'''模組test2.py'''
from test1 import *
print(num)
print(_num)
列印結果:
從上面的結果可以看到:採用 from <模組/包名> import *
方式匯入時,以’_'開頭的變數不會被匯入!!
但是 當以 import moule
的方式匯入時,以’_'開頭的變數就會被匯入,如下:
'''模組test3.py'''
import test1
print(test1.num)
print(test1._num)
列印結果:
2.2 雙下劃線開頭的變數(__XXX)
在Python中,例項的變數名如果以雙下劃線( __ )開頭,就變成了一個私有變數(private),只有內部可以訪問,外部不能訪問。因為Python直譯器對外把 __xxx
變數改成了_classname__xxx
,所以,仍然可以通過_classname__xxx
來訪問__xxx
變數。
例如:
class Student(object):
def __init__(self, name, score, age):
self.__name = name
self.__score = score
self.age = age
st = Student("Yi", 88, 31)
print(st.age) # 返回: 31
print(st.__name) # 報錯: AttributeError: 'Student' object has no attribute '__name'
print(st._Student__name) # 返回: Yi
從上面的結果可以看到:這樣就確保了外部程式碼不能隨意修改物件內部的狀態,這樣通過訪問限制的保護,程式碼更加健壯。
但是如果外部程式碼一定要獲取name
和 score
怎麼辦?可以給Student類增加get_name()
和 get_score()
方法。如下:
class Student(object):
def __init__(self, name, age):
self.__name = name
self.age = age
def get_name(self):
return self.__name
def get_score(self):
return self.__score
st = Student("Yi", 31)
print(st.age) # 返回: 31
#print(st.__name) # 報錯: AttributeError: 'Student' object has no attribute '__name'
print(st._Student__name) # 返回: Yi
print(st.get_name()) # 返回: Yi
print(st.get_score()) # 返回: 88
如果又要允許外部程式碼修改score怎麼辦?可以再給Student類增加set_score()
方法,如下:
class Student(object):
def __init__(self, name, age, score):
self.__name = name
self.__score = score
self.age = age
def get_name(self):
return self.__name
def get_score(self):
return self.__score
def set_score(self, score):
self.__score = score
st = Student("Yi", 31, 88)
print(st.age) # 返回: 31
#print(st.__name) # 報錯: AttributeError: 'Student' object has no attribute '__name'
print(st._Student__name) # 返回: Yi
print(st.get_name()) # 返回: Yi
print("修改前的學生分數:" , st.get_score()) # 返回: 88
st.set_score(95)
print("修改後的學生分數:", st.get_score()) # 返回: 95
也許,我們會有個疑問,直接通過st.score = 99就可以修改啊,這裡為什麼要定義一個方法大費周折呢?
這是因為在方法中,可以對引數做檢查,避免傳入無效的引數。例如:
class Student(object):
def __init__(self, name, age, score):
self.__name = name
self.__score = score
self.age = age
def get_name(self):
return self.__name
def get_score(self):
return self.__score
def set_score(self, score):
if 0 <= score <= 100:
self.__score = score
else:
raise ValueError('bad score')
st = Student("Yi", 31, 88)
print(st.age) # 返回: 31
#print(st.__name) # 報錯: AttributeError: 'Student' object has no attribute '__name'
print(st._Student__name) # 返回: Yi
print(st.get_name()) # 返回: Yi
print("修改前的學生分數:" , st.get_score()) # 返回: 88
st.set_score(95)
print("修改後的學生分數:", st.get_score()) # 返回: 95
st.set_score(120) # 報錯:raise ValueError('bad score')
2.3 雙下劃線開頭和結尾的變數( __ XXX__)
在Python中,類似__xxx__
的變數名,也就是以雙下劃線開頭,並且以雙下劃線結尾的變數,是 特殊變數, 也可以稱之為內建變數,。
特殊變數是可以直接訪問的,不是private變數,、如__init__
,__import__
或是__file__
。所以,最好不要自己定義這類變數。
3 方法中開頭和結尾的雙下劃線
這些是Python的特殊方法名,這僅僅是一種慣例,一種確保Python系統中的名稱不會跟使用者自定義的名稱發生衝突的方式。
通常你可以覆寫這些方法,在Python呼叫它們時,產生你想得到的行為。例如,當寫一個類的時候經常會覆寫__init__
方法。
4 結論
1、_xxx
不能用於from module import *
以單下劃線開頭的表示的是 受保護的(protected) 型別的變數。保護型別變數只能允許其本身與其子類進行訪問。
2、__xxx
雙下劃線開頭的變量表示的是私有型別(private)的變數。只能是允許這個類本身進行訪問了,連子類也不可以
3、__xxx___
定義的是特列方法。像__init__
之類的