【Python】單下劃線與雙下劃線的區別
- 2019 年 10 月 11 日
- 筆記
Python用下劃線作為前綴和後綴指定特殊變量和定義方法,主要有如下四種形式:
- 單下劃線(_)
- 名稱前的單下劃線(如:_name)
- 名稱前的雙下劃線(如:__name)
- 名稱前後的雙下劃線(如:__init__)
單下劃線(_)
只有單劃線的情況,主要有兩種使用場景:
1、在交互式解釋器中,單下劃線「_」代表的是上一條執行語句的結果。如果單下劃線前面沒有語句執行,交互式解釋器將會報單下劃線沒有定義的錯誤。也可以對單下劃線進行賦值操作,這時單下劃線代表賦值的結果。但是一般不建議對單下劃線進行賦值操作,因為單下劃線內建標識符。
>>> _ Traceback (most recent call last): File "<pyshell#0>", line 1, in <module> _ NameError: name '_' is not defined >>> "python" 'python' >>> _ 'python' >>> _="Java" >>> _ 'Java' >>>
2、單下劃線「_」還可以作為特殊的臨時變量。如果一個變量在後面不會再用到,並且不想給這個變量定義名稱,這時就可以用單下劃線作為臨時性的變量。比如對for循環語句遍歷的結果元素並不感興趣,此時就可以用單下劃線表示。
# _ 這個變量在後面不會用到 for _ in range(5): print("Python")
名稱前的單下劃線(如:_name)
當在屬性和方法前面加上單下劃線「_」,用於指定屬性和方法是「私有」的。但是Python不像Java一樣具有私有屬性、方法、類,在屬性和方法之前加單下劃線,只是代表該屬性、方法、類只能在內部使用,是API中非公開的部分。如果用fromimport * 和 fromimport * 時,這些屬性、方法、類將不被導入。
# Test.py 文件 #普通屬性 value="Java" #單下劃線屬性 _otherValue="Python" #普通方法 def method(): print("我是普通方法") #單下劃線方法 def _otherMethod(): print("我是單下劃線方法") #普通類 class PClass(object): def __init__(self): print("普通類的初始化") #單下劃線類 class _WClass(object): def __init__(self): print("單下劃線類的初始化")
將上述的Test.py文件導入,進行測試。
>>> from Test import * >>> value 'Java' >>> _otherValue Traceback (most recent call last): File "<pyshell#4>", line 1, in <module> _otherValue NameError: name '_otherValue' is not defined >>> method() 我是普通方法 >>> _otherMethod() Traceback (most recent call last): File "<pyshell#6>", line 1, in <module> _otherMethod() NameError: name '_otherMethod' is not defined >>> p=PClass() 普通類的初始化 >>> w=_WClass() Traceback (most recent call last): File "<pyshell#8>", line 1, in <module> w=_WClass() NameError: name '_WClass' is not defined
從上面的結果可以看出,不管是屬性、方法和類,只要名稱前面加了單下劃線,都不能導出。
如果對程序進行修改,將在開頭加入__all__,結果會是如何?
# Test.py 文件 #將普通屬性、單下劃線的屬性、方法、和類加入__all__列表 __all__=["value","_otherValue","_otherMethod","_WClass"] #普通屬性 value="Java" #單下劃線屬性 _otherValue="Python" #普通方法 def method(): print("我是普通方法") #單下劃線方法 def _otherMethod(): print("我是單下劃線方法") #普通類 class PClass(object): def __init__(self): print("普通類的初始化") #單下劃線類 class _WClass(object): def __init__(self): print("單下劃線類的初始化")
將上述修改過的Test.py文件導入,進行測試。
>>> from Test import * >>> value 'Java' >>> _otherValue 'Python' >>> method() Traceback (most recent call last): File "<pyshell#4>", line 1, in <module> method() NameError: name 'method' is not defined >>> _otherMethod() 我是單下劃線方法 >>> p=PClass() Traceback (most recent call last): File "<pyshell#6>", line 1, in <module> p=PClass() NameError: name 'PClass' is not defined >>> w= _WClass() 單下劃線類的初始化
__all__是一個字符串列表,不管是普通的還是單下劃線的屬性、方法和類,都將導出來,使用其他不在這個字符列表上的屬性、方法和類,都會報未定義的錯誤。
不管是屬性、方法和類,只要名稱前面加了單下劃線,都不能導入。除非是模塊或包中的「__all__」列表顯式地包含了它們。
名稱前的雙下劃線(如:__name)
我們先看看下面的程序:
class Method(object): # 構造器方法 def __init__(self, name): # 雙下劃線屬性 self.__name = name # 普通方法 def sayhello(self): print("Method say hello!") # 雙下劃線方法 def __sayhi(self): print("Method say hi!") # 初始化Method m = Method("Python") # 調用sayhello方法 m.sayhello() # 調用sayhi方法 m.__sayhi() # 輸出屬性__name print(m.__name)
上面的程序定義了一個類,這個類有三個方法,一個構造器方法,一個普通方法,一個雙下劃線方法,以及包括一個雙下劃線的屬性。上面的結果輸出的是什麼?很多讀者可能認為輸出的結果如下:
Method say hello! Method say hi! Python
那麼恭喜你,上面的輸出結果是錯誤的,實際輸出的結果為:
Method say hello! Traceback (most recent call last): File "<encoding error>", line 18, in <module> AttributeError: 'Method' object has no attribute '__sayhi'
實際上,當對象調用__sayhi()方法時,將會報Method類沒有這個方法屬性的錯誤。那如何去調用以雙下劃線開頭的方法和屬性?Python這樣設計的目的是什麼?
首先回答第一個問題,讀者看完下面的程序就知道怎麼調用了。
class Method(object): def __init__(self, name): self.__name = name def sayhello(self): print("Method say hello!") def __sayhi(self): print("Method say hi!") # 初始化Method m = Method("Python") # 調用sayhello方法 m.sayhello() # 調用sayhi方法 #m.__sayhi() m._Method__sayhi() # 輸出屬性__name #print(m.__name) print(m._Method__name)
輸出結果如下:
Method say hello! Method say hi! Python
我們從上面的程序中可以很清楚的看到,如果要調用以雙下劃線開頭的方法和屬性,只要以「類名_方法(屬性)」的形式就可以實現方法或者屬性的訪問了。類前面是單下劃線,類名後面是雙下劃線,然後再加上方法或者屬性。但是並不建議調用,因為這是Python內部進行調用的形式。
回答完第一個問題,我們看看第二個問題,Python這樣設計的目的是什麼?
有很多人認為,Python以雙下劃線開頭的方法和屬性表示私有的方法和屬性,實際上這樣的理解不太準確,也不能說完全錯誤的。但是這並不是Python設計的目的和初衷,我們先看看下面一段程序和程序運行結果:
class AMethod(object): def __method(self): print("__method in class Amethod!") def method(self): self.__method() print("anthod method in class AMethod!") class BMethod(AMethod): def __method(self): print("__method in class Bmethod!") if __name__=="__main__": print("調用AMethod的method方法") a = AMethod() a.method() print("調用BMethod的method方法") b = BMethod() b.method()
上面的程序定義了兩個類,一個是AMethod類,另外一個是繼承了AMethod類的BMethod類。在AMethod類中,定義了兩個方法,一個是以雙下劃線開頭的__method方法,另外一個是普通方法。在BMethod類中,重寫了AMethod類中的__method方法。
程序運行結果:
調用AMethod的method方法 __method in class Amethod! anthod method in class AMethod! 調用BMethod的method方法 __method in class Amethod! anthod method in class AMethod!
運行結果並不是我們想要的結果,b.method()並沒有調用BMethod類的__method方法,而這個設計的實際目的是為了避免父類的方法被子類輕易的覆蓋。
名稱前後的雙下劃線(如:__ init __)
在Python類中,我們可以常常看到類似於「__ init ___」的方法,這表示在Python內部調用的方法,一般不建議在程序中調用。比如,當調用len()方法時,實際上調用了 Python中內部的 ___len ___方法,雖然不建議調用這種以雙下劃線開頭以及結尾的方法,但是可以對這些方法進行重寫。比如下面的例子:
class Number(object): def __init__(self, number): self.number = number def __add__(self, number): # 重寫方法,返回兩個數的差值 return self.number - number def __sub__(self, number): # 重寫方法,返回兩個數的和 return self.number + number def __str__(self): # 重寫方法,返回字符串 return str(self.number) num = Number(100) print(num) # 100 調用了__str__方法 print(num+50) # 50 + 調用了__add__方法 print(num-20) # 120 -調用了__sub__方法
相信看了上面所有對Python中下劃線作用的講解,完全能夠理解上述四種下劃線所表示的意義。最後將對上面的,進行總結。
總結
- 單下劃線(_): 在交互解釋器中,表示上一條語句執行輸出的結果。另外,單下劃線還可以作為特殊的臨時變量,表示在後面將不會在用到這個變量。
- 名稱前的單下劃線:只能在內部使用,是API中非公開的部分,不能被import * 和 fromimport *導入程序中,除非在all列表中包含了以單下劃線開頭的屬性、方法以及類。
- 名稱前的雙下劃線:以雙下劃線開頭的屬性、方法表示避免父類的屬性和方法被子類輕易的覆蓋,一般不建議這樣定義屬性和方法,除非你自己將要做什麼。
- 名稱前後的雙下劃線:這類方法是Python內部定義的方法,你可以重寫這些方法,這樣Python就可以調用這個重寫的方法以及利用操作符。