前言
Python 的代碼風(fēng)格由 PEP 8 描述。這個(gè)文檔描述了 Python 編程風(fēng)格的方方面面。在遵守這個(gè)文檔的條件下,不同程序員編寫(xiě)的 Python 代碼可以保持最大程度的相似風(fēng)格。這樣就易于閱讀,易于在程序員之間交流。
我們大家在學(xué)習(xí)Python的時(shí)候,好像很多人都不理解為什么在方法(method)前面會(huì)加好幾個(gè)下劃線,有時(shí)甚至兩邊都會(huì)加,比如像__this__這種。在我看到上面的文章之前,我一直以為Python中這些下劃線的作用就像Golang中方法/函數(shù)的大小寫(xiě)一樣,或是一些其他語(yǔ)言中的private、public的作用一樣,但仔細(xì)深究,這不全是Python這樣設(shè)計(jì)的初衷。
下面我們具體分析,話不多說(shuō)了,來(lái)一起看看吧。
單下劃線開(kāi)頭
我們經(jīng)常看到方法或者屬性前面加了單下劃線,并認(rèn)為它表示該方法或者屬性是該類型(Python和Golang一樣,不光類可以有方法,很多類型甚至基本類型也可以定義方法)的私有方法或?qū)傩浴5鋵?shí)在Python中不存在真正意義上的私有方法或者屬性,前面加單下劃線_只是表示你不應(yīng)該去訪問(wèn)這個(gè)方法或者屬性,因?yàn)樗皇茿PI的一部分。
舉個(gè)例子:
Pythonclass 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)
該代碼片段來(lái)自Django源碼(django/forms/forms.py)。這段代碼的設(shè)計(jì)就是errors屬性是對(duì)外API的一部分,如果你想獲取錯(cuò)誤詳情,應(yīng)該訪問(wèn)errors屬性,而不是(也不應(yīng)該)訪問(wèn)_get_errors方法。
雙下劃線開(kāi)頭
之前很多人跟我說(shuō)Python中雙下劃線開(kāi)頭表示私有,我在很多地方也見(jiàn)到這樣的說(shuō)法。這樣理解可能也不能說(shuō)錯(cuò),但這不是Python設(shè)計(jì)雙下劃線開(kāi)頭的初衷和目的,Python設(shè)計(jì)此的真正目的僅僅是為了避免子類覆蓋父類的方法。
我們看個(gè)例子:
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 AI'm another method in class AI'm a method in class AI'm another method in class Bsituation 2:I'm a method in class A
這里有兩個(gè)點(diǎn)需要注意:
A類中我們定義了__method()、method_x和method()三個(gè)方法;然后我們重新定義一個(gè)類B,繼承自A,并且在B類中覆寫(xiě)(override)了其父類的__method()和method_x方法,但是從輸出結(jié)果看,B對(duì)象調(diào)用method()方法時(shí)調(diào)用了其父類A的__method()方法和自己的method_x()方法。也就是說(shuō),__method()覆寫(xiě)沒(méi)有生效,而method_x()覆寫(xiě)生效了。而這也正是Python設(shè)計(jì)雙下劃線開(kāi)頭的唯一目的。
這一點(diǎn)也可在Python官方說(shuō)明中得到答案:https://www.python.org/dev/peps/pep-0008/#method-names-and-instance-variables。
前面我們就說(shuō)了,Python中不存在真正意義上的私有變量。對(duì)于雙下劃線開(kāi)頭的方法和屬性雖然我們不能直接引用,那是因?yàn)镻ython默認(rèn)在其前面加了前綴_類名,所以就像situation 2下面的代碼,雖然我們不能用a直接訪問(wèn)__method(),但卻可以加上前綴去訪問(wèn),即_A__method()。
開(kāi)頭結(jié)尾雙下劃線
一般來(lái)說(shuō)像__this__這種開(kāi)頭結(jié)尾都加雙下劃線的方法表示這是Python自己調(diào)用的,你不要調(diào)用。比如我們可以調(diào)用len()函數(shù)來(lái)求長(zhǎng)度,其實(shí)它后臺(tái)是調(diào)用了__len__()方法。一般我們應(yīng)該使用len,而不是直接使用__len__():
a = [1, 2, 3]print(len(a)) print(a.__len__()) # 和上面等效num = 10print(num + 10)print(num.__add__(10)) # 和上面等效
我們一般稱__len__()這種方法為magic methods,一些操作符后臺(tái)調(diào)用的也是也是這些magic methods,比如+后臺(tái)調(diào)用的是__add__,-調(diào)用的是__sub__,所以這種機(jī)制使得我們可以在自己的類中覆寫(xiě)操作符(見(jiàn)后面例子)。另外,有的時(shí)候這種開(kāi)頭結(jié)尾雙下劃線方法僅僅是某些特殊場(chǎng)景的回調(diào)函數(shù),比如__init__()會(huì)在對(duì)象的初始化時(shí)調(diào)用,__new__()會(huì)在構(gòu)建一個(gè)實(shí)例的時(shí)候調(diào)用等等。下面我們看兩個(gè)例子:
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: 10print(num + 5) # output is: 5print(num - 20) # output is: 30
在上面這個(gè)例子中,我們覆寫(xiě)了+和-操作符,將他們的功能交換了。再看個(gè)例子:
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這個(gè)例子中,因?yàn)槲覀儗?shí)現(xiàn)了__len__(),所以Room對(duì)象也可以使用len函數(shù)了。
所有此類的方法都在這里有說(shuō)明:documentation.
結(jié)論
注:本文大部分內(nèi)容參考自Difference between _ , and __xx in Python .
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)VEVB武林網(wǎng)的支持。
新聞熱點(diǎn)
疑難解答
圖片精選