溫馨提示×

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

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

不知道這5種下劃線的含義,你就不算真的會(huì)Python!

發(fā)布時(shí)間:2020-09-14 06:03:47 來源:腳本之家 閱讀:194 作者:51Testing軟件測(cè)試網(wǎng) 欄目:開發(fā)技術(shù)

什么是 Python? Python 之父 Guido van Rossum 說:Python是一種高級(jí)程序語言,其核心設(shè)計(jì)哲學(xué)是代碼可讀性和語法,能夠讓程序員用很少的代碼來表達(dá)自己的想法。

對(duì)于我來說,學(xué)習(xí) Python 的首要原因是,Python 是一種可以優(yōu)雅編程的語言。它能夠簡(jiǎn)單自然地寫出代碼和實(shí)現(xiàn)我的想法。

另一個(gè)原因是我們可以將 Python 用在很多地方:人工智能、數(shù)據(jù)科學(xué)、Web 開發(fā)和機(jī)器學(xué)習(xí)等都可以使用 Python 來開發(fā)。

國(guó)慶期間后臺(tái)有小伙伴留言問我:“Python變量和方法名稱中單下劃線和雙下劃線的含義是什么?”我想一些初學(xué)者或者經(jīng)驗(yàn)尚淺的朋友一定也會(huì)有這個(gè)疑問,所以今天我就來跟大家聊聊Python中這個(gè)很重要的“下劃線”!

不知道這5種下劃線的含義,你就不算真的會(huì)Python!

單下劃線和雙下劃線在Python變量和方法名稱中都各有其含義。有一些含義僅僅是依照約定,被視作是對(duì)程序員的提示 - 而有一些含義是由Python解釋器嚴(yán)格執(zhí)行的。

那么,下面就為大家介紹一下Python中單下劃線和雙下劃線("dunder")的各種含義和命名約定,名稱修飾(name mangling)的工作原理,以及它如何影響你自己的Python類。

下面將討論以下五種下劃線模式和命名約定,以及它們?nèi)绾斡绊慞ython程序的行為:

單前導(dǎo)下劃線:_var

單末尾下劃線:var_

雙前導(dǎo)下劃線:__var

雙前導(dǎo)和末尾下劃線:__var__

單下劃線:_

1、單前導(dǎo)下劃線 _var

程序員使用名稱前的單下劃線,用于指定該名稱屬性為“私有”。這有點(diǎn)類似于慣例,為了使其他人(或你自己)使用這些代碼時(shí)將會(huì)知道以“_”開頭的名稱只供內(nèi)部使用。正如Python文檔中所述:

以下劃線“_”為前綴的名稱(如_spam)應(yīng)該被視為API中非公開的部分(不管是函數(shù)、方法還是數(shù)據(jù)成員)。此時(shí),應(yīng)該將它們看作是一種實(shí)現(xiàn)細(xì)節(jié),在修改它們時(shí)無需對(duì)外部通知。

正如上面所說,這確實(shí)類似一種慣例,因?yàn)樗鼘?duì)解釋器來說確實(shí)有一定的意義,如果你寫了代碼“from <模塊/包名> import *”,那么以“_”開頭的名稱都不會(huì)被導(dǎo)入,除非模塊或包中的“__all__”列表顯式地包含了它們。

看看下面的例子:

class Test:
def __init__(self):
self.foo = 11
self._bar = 23

如果你實(shí)例化此類,并嘗試訪問在__init__構(gòu)造函數(shù)中定義的foo和_bar屬性,會(huì)發(fā)生什么情況? 讓我們來看看:

>>> t = Test()
>>> t.foo
11
>>> t._bar
23

你會(huì)看到_bar中的單個(gè)下劃線并沒有阻止我們“進(jìn)入”類并訪問該變量的值。

這是因?yàn)镻ython中的單個(gè)下劃線前綴僅僅是一個(gè)約定 - 至少相對(duì)于變量和方法名而言。

但是,前導(dǎo)下劃線的確會(huì)影響從模塊中導(dǎo)入名稱的方式。

假設(shè)你在一個(gè)名為my_module的模塊中有以下代碼:

# This is my_module.py:
def external_func():
return 23
def _internal_func():
return 42

現(xiàn)在,如果使用通配符從模塊中導(dǎo)入所有名稱,則Python不會(huì)導(dǎo)入帶有前導(dǎo)下劃線的名稱(除非模塊定義了覆蓋此行為的__all__列表):

>>> from my_module import *
>>> external_func()
23
>>> _internal_func()
NameError: "name '_internal_func' is not defined"

順便說一下,應(yīng)該避免通配符導(dǎo)入,因?yàn)樗鼈兪姑Q空間中存在哪些名稱不清楚。 為了清楚起見,堅(jiān)持常規(guī)導(dǎo)入更好。

與通配符導(dǎo)入不同,常規(guī)導(dǎo)入不受前導(dǎo)單個(gè)下劃線命名約定的影響:

>>> import my_module
>>> my_module.external_func()
23
>>> my_module._internal_func()
42

我們知道這一點(diǎn)可能有點(diǎn)令人困惑。 如果你遵循PEP 8推薦,避免通配符導(dǎo)入,那么你真正需要記住的只有這個(gè):

單個(gè)下劃線是一個(gè)Python命名約定,表示這個(gè)名稱是供內(nèi)部使用的。 它通常不由Python解釋器強(qiáng)制執(zhí)行,僅僅作為一種對(duì)程序員的提示。

2、單末尾下劃線 var_

有時(shí)候,一個(gè)變量的最合適的名稱已經(jīng)被一個(gè)關(guān)鍵字所占用。 因此,像class或def這樣的名稱不能用作Python中的變量名稱。 在這種情況下,你可以附加一個(gè)下劃線來解決命名沖突:

>>> def make_object(name, class):
SyntaxError: "invalid syntax"
>>> def make_object(name, class_):
... pass

總之,單個(gè)末尾下劃線(后綴)是一個(gè)約定,用來避免與Python關(guān)鍵字產(chǎn)生命名沖突。 PEP 8解釋了這個(gè)約定。

3、雙前導(dǎo)下劃線 __var

名稱(具體為一個(gè)方法名)前雙下劃線(__)的用法并不是一種慣例,對(duì)解釋器來說它有特定的意義。Python中的這種用法是為了避免與子類定義的名稱沖突。Python文檔指出,“__spam”這種形式(至少兩個(gè)前導(dǎo)下劃線,最多一個(gè)后續(xù)下劃線)的任何標(biāo)識(shí)符將會(huì)被“_classname__spam”這種形式原文取代,在這里“classname”是去掉前導(dǎo)下劃線的當(dāng)前類名。

例如下面的例子:

>>> class A(object):
... def _internal_use(self):
... pass
... def __method_name(self):
... pass
...
>>> dir(A())
['_A__method_name', ..., '_internal_use']

正如所預(yù)料的,“_internal_use”并未改變,而“__method_name”卻被變成了“_ClassName__method_name”。此時(shí),如果你創(chuàng)建A的一個(gè)子類B,那么你將不能輕易地覆寫A中的方法“__method_name”。

>>> class B(A):
... def __method_name(self):
... pass
...
>>> dir(B())

['_A__method_name', '_B__method_name', ..., '_internal_use']

這里的功能幾乎和Java中的final方法和C++類中標(biāo)準(zhǔn)方法(非虛方法)一樣。

4、雙前導(dǎo)和雙末尾下劃線 _var_

也許令人驚訝的是,如果一個(gè)名字同時(shí)以雙下劃線開始和結(jié)束,則不會(huì)應(yīng)用名稱修飾。 由雙下劃線前綴和后綴包圍的變量不會(huì)被Python解釋器修改:

class PrefixPostfixTest:
def __init__(self):
self.__bam__ = 42
>>> PrefixPostfixTest().__bam__
42

但是,Python保留了有雙前導(dǎo)和雙末尾下劃線的名稱,用于特殊用途。 這樣的例子有,__init__對(duì)象構(gòu)造函數(shù),或__call__ --- 它使得一個(gè)對(duì)象可以被調(diào)用。

這些dunder方法通常被稱為神奇方法 - 但Python社區(qū)中的許多人都不喜歡這種方法。

最好避免在自己的程序中使用以雙下劃線(“dunders”)開頭和結(jié)尾的名稱,以避免與將來Python語言的變化產(chǎn)生沖突。

5、單下劃線 _

通常情況下,會(huì)在以下3種場(chǎng)景中使用:

1、在解釋器中:在這種情況下,“_”代表交互式解釋器會(huì)話中上一條執(zhí)行的語句的結(jié)果。這種用法首先被標(biāo)準(zhǔn)CPython解釋器采用,然后其他類型的解釋器也先后采用。

>>> _ Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name '_' is not defined
>>> 42
>>> _
42
>>> 'alright!' if _ else ':('
'alright!'
>>> _
'alright!'

2、作為一個(gè)名稱:這與上面一點(diǎn)稍微有些聯(lián)系,此時(shí)“_”作為臨時(shí)性的名稱使用。這樣,當(dāng)其他人閱讀你的代碼時(shí)將會(huì)知道,你分配了一個(gè)特定的名稱,但是并不會(huì)在后面再次用到該名稱。例如,下面的例子中,你可能對(duì)循環(huán)計(jì)數(shù)中的實(shí)際值并不感興趣,此時(shí)就可以使用“_”。

n = 42
for _ in range(n):
do_something()

3、國(guó)際化:也許你也曾看到”_“會(huì)被作為一個(gè)函數(shù)來使用。這種情況下,它通常用于實(shí)現(xiàn)國(guó)際化和本地化字符串之間翻譯查找的函數(shù)名稱,這似乎源自并遵循相應(yīng)的C約定。

例如,在Django文檔“轉(zhuǎn)換”章節(jié)中,你將能看到如下代碼:

from django.utils.translation import ugettext as _
from django.http import HttpResponse
def my_view(request):
output = _("Welcome to my site.")
return HttpResponse(output)

可以發(fā)現(xiàn),場(chǎng)景二和場(chǎng)景三中的使用方法可能會(huì)相互沖突,所以我們需要避免在使用“_”作為國(guó)際化查找轉(zhuǎn)換功能的代碼塊中同時(shí)使用“_”作為臨時(shí)名稱。

總結(jié):

Python下劃線命名模式 - 小結(jié)

以下是一個(gè)簡(jiǎn)短的小結(jié),即“速查表”,羅列了本文中談到的五種Python下劃線模式的含義:

不知道這5種下劃線的含義,你就不算真的會(huì)Python!

以上所述是小編給大家介紹的不知道這5種下劃線的含義,你就不算真的會(huì)Python!,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)億速云網(wǎng)站的支持!

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

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

AI