溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python中單、雙下劃線的區(qū)別總結(jié)

發(fā)布時間:2020-08-11 21:43:20 來源:ITPUB博客 閱讀:173 作者:蘄喻 欄目:編程語言

前言

Python 的代碼風(fēng)格由 PEP 8 描述。這個文檔描述了 Python 編程風(fēng)格的方方面面。在遵守這個文檔的條件下,不同程序員編寫的 Python 代碼可以保持最大程度的相似風(fēng)格。這樣就易于閱讀,易于在程序員之間交流。

我們大家在學(xué)習(xí)Python的時候,好像很多人都不理解為什么在方法(method)前面會加好幾個下劃線,有時甚至兩邊都會加,比如像__this__這種。在我看到上面的文章之前,我一直以為Python中這些下劃線的作用就像Golang中方法/函數(shù)的大小寫一樣,或是一些其他語言中的private、public的作用一樣,但仔細(xì)深究,這不全是Python這樣設(shè)計的初衷。

下面我們具體分析,話不多說了,來一起看看吧。

單下劃線開頭

我們經(jīng)??吹椒椒ɑ蛘邔傩郧懊婕恿藛蜗聞澗€,并認(rèn)為它表示該方法或者屬性是該類型(Python和Golang一樣,不光類可以有方法,很多類型甚至基本類型也可以定義方法)的私有方法或?qū)傩?。但其實在Python中不存在真正意義上的私有方法或者屬性,前面加單下劃線_只是表示你不應(yīng)該去訪問這個方法或者屬性,因為它不是API的一部分。

舉個例子:

class BaseForm(StrAndUnicode):
    ...
    
    def _get_errors(self):
        "Returns an ErrorDict for the data provided for the form"
        if self._errors is None:
            self.full_clean()
            return self._errors
    errors = property(_get_errors)

該代碼片段來自Django源碼(django/forms/forms.py)。這段代碼的設(shè)計就是errors屬性是對外API的一部分,如果你想獲取錯誤詳情,應(yīng)該訪問errors屬性,而不是(也不應(yīng)該)訪問_get_errors方法。

雙下劃線開頭

之前很多人跟我說Python中雙下劃線開頭表示私有,我在很多地方也見到這樣的說法。這樣理解可能也不能說錯,但這不是Python設(shè)計雙下劃線開頭的初衷和目的,Python設(shè)計此的真正目的僅僅是為了避免子類覆蓋父類的方法。

我們看個例子:

class A(object):
    def __method(self):
        print("I'm a method in class A")
    def method_x(self):
        print("I'm another method in class A\n")
 
    def method(self):
        self.__method()
        self.method_x()
class B(A):
    def __method(self):
        print("I'm a method in class B")
 
    def method_x(self):
        print("I'm another method in class B\n")
if __name__ == '__main__':
    print("situation 1:")
    a = A()
    a.method()
    
    b = B()
    b.method()
    
    print("situation 2:")
    #a.__method()
    a._A__method()

執(zhí)行結(jié)果:

situation 1:
I'm a method in class A
I'm another method in class A
 
I'm a method in class A
I'm another method in class B
 
situation 2:
I'm a method in class A

這里有兩個點需要注意:

A類中我們定義了__method()、method_x和method()三個方法;然后我們重新定義一個類B,繼承自A,并且在B類中覆寫(override)了其父類的__method()和method_x方法,但是從輸出結(jié)果看,B對象調(diào)用method()方法時調(diào)用了其父類A的__method()方法和自己的method_x()方法。也就是說,__method()覆寫沒有生效,而method_x()覆寫生效了。而這也正是Python設(shè)計雙下劃線開頭的唯一目的。

這一點也可在Python官方說明中得到答案: https://www.python.org/dev/peps/pep-0008/#method-names-and-instance-variables 。

前面我們就說了,Python中不存在真正意義上的私有變量。對于雙下劃線開頭的方法和屬性雖然我們不能直接引用,那是因為Python默認(rèn)在其前面加了前綴_類名,所以就像situation 2下面的代碼,雖然我們不能用a直接訪問__method(),但卻可以加上前綴去訪問,即_A__method()。

開頭結(jié)尾雙下劃線

一般來說像__this__這種開頭結(jié)尾都加雙下劃線的方法表示這是Python自己調(diào)用的,你不要調(diào)用。比如我們可以調(diào)用len()函數(shù)來求長度,其實它后臺是調(diào)用了__len__()方法。一般我們應(yīng)該使用len,而不是直接使用__len__():

a = [1, 2, 3]
print(len(a)) 
print(a.__len__()) # 和上面等效
 
num = 10
print(num + 10)
print(num.__add__(10)) # 和上面等效

我們一般稱__len__()這種方法為magic methods,一些操作符后臺調(diào)用的也是也是這些magic methods,比如+后臺調(diào)用的是__add__,-調(diào)用的是__sub__,所以這種機制使得我們可以在自己的類中覆寫操作符(見后面例子)。另外,有的時候這種開頭結(jié)尾雙下劃線方法僅僅是某些特殊場景的回調(diào)函數(shù),比如__init__()會在對象的初始化時調(diào)用,__new__()會在構(gòu)建一個實例的時候調(diào)用等等。下面我們看兩個例子:

class CrazyNumber(object):
    def __init__(self, n):
        self.n = n 
    def __add__(self, other): 
        return self.n - other 
    def __sub__(self, other): 
        return self.n + other 
    def __str__(self): 
        return str(self.n) 
num = CrazyNumber(10) 
print(num) # output is: 10
print(num + 5) # output is: 5
print(num - 20) # output is: 30

在上面這個例子中,我們覆寫了+和-操作符,將他們的功能交換了。再看個例子:

class Room(object):
    def __init__(self):
        self.people = [] 
    def add(self, person): 
        self.people.append(person) 
    def __len__(self): 
        return len(self.people)
room = Room() 
room.add("Igor") 
print(len(room)) # output is: 1

這個例子中,因為我們實現(xiàn)了__len__(),所以Room對象也可以使用len函數(shù)了。

所有此類的方法都在這里有說明:documentation.

結(jié)論

  • 使用單下劃線(_one_underline)開頭表示方法不是API的一部分,不要直接訪問(雖然語法上訪問也沒有什么問題)。
  • 使用雙下劃線開頭(__two_underlines)開頭表示子類不能覆寫該方法。除非你真的知道你在干什么,否則不要使用這種方式。
  • 當(dāng)你想讓自己定義的對象也可以像Python內(nèi)置的對象一樣使用Python內(nèi)置的一些函數(shù)或操作符(比如len、add、+、-、==等)時,你可以定義該類方法。
  • 當(dāng)然還有些屬性只在末尾加了但下劃線,這僅僅是為了避免我們起的一些名字和Python保留關(guān)鍵字沖突,沒有特殊含義。

注: 本文大部分內(nèi)容參考自 Difference between _  ,  and __xx in Python  .

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI