​【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就可以調用這個重寫的方法以及利用操作符。