Python中下劃線的不同用法總結(jié)

本文介紹了Python中單下劃線和雙下劃線("dunder")的各種含義和命名約定负蚊,名稱修飾(name mangling)的工作原理饰及,以及它如何影響你自己的Python類。

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

如果你想知道“Python變量和方法名稱中單下劃線和雙下劃線的含義是什么挽唉?”滤祖,我會盡我所能在這里為你解答。

在本文中瓶籽,我將討論以下五種下劃線模式和命名約定匠童,以及它們?nèi)绾斡绊慞ython程序的行為:

·單前導下劃線:_var

·單末尾下劃線:var_

·雙前導下劃線:__var

·雙前導和末尾下劃線:__var__

·單下劃線:_

在文章結(jié)尾處,你可以找到一個簡短的“速查表”塑顺,總結(jié)了五種不同的下劃線命名約定及其含義俏让,可讓你親身體驗它們的行為。

讓我們馬上開始茬暇!

1. 單前導下劃線 _var

當涉及到變量和方法名稱時,單個下劃線前綴有一個約定俗成的含義寡喝。 它是對程序員的一個提示 - 意味著Python社區(qū)一致認為它應該是什么意思糙俗,但程序的行為不受影響。

下劃線前綴的含義是告知其他程序員:以單個下劃線開頭的變量或方法僅供內(nèi)部使用预鬓。 該約定在PEP 8中有定義巧骚。

這不是Python強制規(guī)定的。 Python不像Java那樣在“私有”和“公共”變量之間有很強的區(qū)別格二。 這就像有人提出了一個小小的下劃線警告標志劈彪,說:

“嘿,這不是真的要成為類的公共接口的一部分顶猜。不去管它就好沧奴。“

看看下面的例子:

classTest:

???def?__init__(self):

???????self.foo?=?11

???????self._bar?=?23

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

>>>?t?=?Test()

>>>?t.foo

11

>>>?t._bar

23

你會看到_bar中的單個下劃線并沒有阻止我們“進入”類并訪問該變量的值。

這是因為Python中的單個下劃線前綴僅僅是一個約定 - 至少相對于變量和方法名而言疮绷。

但是翰舌,前導下劃線的確會影響從模塊中導入名稱的方式。

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

#?This?is?my_module.py:


def?external_func():

???return23


def?_internal_func():

???return42

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

>>>?from?my_module?import?*

>>>?external_func()

23

>>>?_internal_func()

NameError:?"name?'_internal_func'?is?not?defined"

順便說一下,應該避免通配符導入只冻,因為它們使名稱空間中存在哪些名稱不清楚庇麦。 為了清楚起見,堅持常規(guī)導入更好属愤。

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

>>>?import?my_module

>>>?my_module.external_func()

23

>>>?my_module._internal_func()

42

我知道這一點可能有點令人困惑。 如果你遵循PEP 8推薦住诸,避免通配符導入驾胆,那么你真正需要記住的只有這個:

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

2. 單末尾下劃線 var_

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

>>>?def?make_object(name,?class):

SyntaxError:?"invalid?syntax"


>>>?def?make_object(name,?class_):

...????pass

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

3. 雙前導下劃線 __var

到目前為止沮脖,我們所涉及的所有命名模式的含義金矛,來自于已達成共識的約定。 而對于以雙下劃線開頭的Python類的屬性(包括變量和方法)勺届,情況就有點不同了驶俊。

雙下劃線前綴會導致Python解釋器重寫屬性名稱,以避免子類中的命名沖突免姿。

這也叫做名稱修飾(name mangling) - 解釋器更改變量的名稱饼酿,以便在類被擴展的時候不容易產(chǎn)生沖突。

我知道這聽起來很抽象胚膊。 因此嗜湃,我組合了一個小小的代碼示例來予以說明:

classTest:

???def?__init__(self):

???????self.foo?=?11

???????self._bar?=?23

???????self.__baz?=?23

讓我們用內(nèi)置的dir()函數(shù)來看看這個對象的屬性:

>>>?t?=?Test()

>>>?dir(t)

['_Test__baz',?'__class__',?'__delattr__',?'__dict__',?'__dir__',

'__doc__',?'__eq__',?'__format__',?'__ge__',?'__getattribute__',

'__gt__',?'__hash__',?'__init__',?'__le__',?'__lt__',?'__module__',

'__ne__',?'__new__',?'__reduce__',?'__reduce_ex__',?'__repr__',

'__setattr__',?'__sizeof__',?'__str__',?'__subclasshook__',

'__weakref__',?'_bar',?'foo']

以上是這個對象屬性的列表奈应。 讓我們來看看這個列表,并尋找我們的原始變量名稱foo购披,_bar和__baz - 我保證你會注意到一些有趣的變化杖挣。

·self.foo變量在屬性列表中顯示為未修改為foo。

·self._bar的行為方式相同 - 它以_bar的形式顯示在類上刚陡。 就像我之前說過的惩妇,在這種情況下,前導下劃線僅僅是一個約定筐乳。 給程序員一個提示而已歌殃。

·然而,對于self.__baz而言蝙云,情況看起來有點不同氓皱。 當你在該列表中搜索__baz時,你會看不到有這個名字的變量勃刨。

__baz出什么情況了波材?

如果你仔細觀察,你會看到此對象上有一個名為_Test__baz的屬性身隐。 這就是Python解釋器所做的名稱修飾廷区。 它這樣做是為了防止變量在子類中被重寫。

讓我們創(chuàng)建另一個擴展Test類的類贾铝,并嘗試重寫構(gòu)造函數(shù)中添加的現(xiàn)有屬性:

classExtendedTest(Test):

???def?__init__(self):

???????super().__init__()

???????self.foo?=?'overridden'

???????self._bar?=?'overridden'

???????self.__baz?=?'overridden'

現(xiàn)在隙轻,你認為foo,_bar和__baz的值會出現(xiàn)在這個ExtendedTest類的實例上嗎垢揩? 我們來看一看:

>>>?t2?=?ExtendedTest()

>>>?t2.foo

'overridden'

>>>?t2._bar

'overridden'

>>>?t2.__baz

AttributeError:?"'ExtendedTest'?object?has?no?attribute?'__baz'"

等一下玖绿,當我們嘗試查看t2 .__ baz的值時,為什么我們會得到AttributeError叁巨? 名稱修飾被再次觸發(fā)了斑匪! 事實證明,這個對象甚至沒有__baz屬性:

>>>?dir(t2)

['_ExtendedTest__baz',?'_Test__baz',?'__class__',?'__delattr__',

'__dict__',?'__dir__',?'__doc__',?'__eq__',?'__format__',?'__ge__',

'__getattribute__',?'__gt__',?'__hash__',?'__init__',?'__le__',

'__lt__',?'__module__',?'__ne__',?'__new__',?'__reduce__',

'__reduce_ex__',?'__repr__',?'__setattr__',?'__sizeof__',?'__str__',

'__subclasshook__',?'__weakref__',?'_bar',?'foo',?'get_vars']

正如你可以看到__baz變成_ExtendedTest__baz以防止意外修改:

>>>?t2._ExtendedTest__baz

'overridden'

但原來的_Test__baz還在:

>>>?t2._Test__baz

42

雙下劃線名稱修飾對程序員是完全透明的俘种。 下面的例子證實了這一點:

classManglingTest:

???def?__init__(self):

???????self.__mangled?=?'hello'


???def?get_mangled(self):

???????returnself.__mangled


>>>?ManglingTest().get_mangled()

'hello'

>>>?ManglingTest().__mangled

AttributeError:?"'ManglingTest'?object?has?no?attribute?'__mangled'"

名稱修飾是否也適用于方法名稱? 是的绝淡,也適用宙刘。名稱修飾會影響在一個類的上下文中,以兩個下劃線字符("dunders")開頭的所有名稱:

classMangledMethod:

???def?__method(self):

???????return42


???def?call_it(self):

???????returnself.__method()


>>>?MangledMethod().__method()

AttributeError:?"'MangledMethod'?object?has?no?attribute?'__method'"

>>>?MangledMethod().call_it()

42

這是另一個也許令人驚訝的運用名稱修飾的例子:

_MangledGlobal__mangled?=?23


classMangledGlobal:

???def?test(self):

???????return__mangled


>>>?MangledGlobal().test()

23

在這個例子中牢酵,我聲明了一個名為_MangledGlobal__mangled的全局變量悬包。然后我在名為MangledGlobal的類的上下文中訪問變量。由于名稱修飾馍乙,我能夠在類的test()方法內(nèi)布近,以__mangled來引用_MangledGlobal__mangled全局變量垫释。

Python解釋器自動將名稱__mangled擴展為_MangledGlobal__mangled,因為它以兩個下劃線字符開頭撑瞧。這表明名稱修飾不是專門與類屬性關(guān)聯(lián)的棵譬。它適用于在類上下文中使用的兩個下劃線字符開頭的任何名稱。

有很多要吸收的內(nèi)容吧预伺。

老實說订咸,這些例子和解釋不是從我腦子里蹦出來的。我作了一些研究和加工才弄出來酬诀。我一直使用Python脏嚷,有很多年了,但是像這樣的規(guī)則和特殊情況并不總是浮現(xiàn)在腦海里瞒御。

有時候程序員最重要的技能是“模式識別”父叙,而且知道在哪里查閱信息。如果您在這一點上感到有點不知所措肴裙,請不要擔心趾唱。慢慢來,試試這篇文章中的一些例子践宴。

讓這些概念完全沉浸下來鲸匿,以便你能夠理解名稱修飾的總體思路,以及我向您展示的一些其他的行為阻肩。如果有一天你和它們不期而遇带欢,你會知道在文檔中按什么來查。

4. 雙前導和雙末尾下劃線 _var_

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

classPrefixPostfixTest:

???def?__init__(self):

???????self.__bam__?=?42


>>>?PrefixPostfixTest().__bam__

42

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

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

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

5.單下劃線 _

按照習慣逢渔,有時候單個獨立下劃線是用作一個名字肋坚,來表示某個變量是臨時的或無關(guān)緊要的。

例如,在下面的循環(huán)中智厌,我們不需要訪問正在運行的索引诲泌,我們可以使用“_”來表示它只是一個臨時值:

>>>?for_?in?range(32):

...????print('Hello,?World.')

你也可以在拆分(unpacking)表達式中將單個下劃線用作“不關(guān)心的”變量,以忽略特定的值铣鹏。 同樣敷扫,這個含義只是“依照約定”,并不會在Python解釋器中觸發(fā)特殊的行為吝沫。 單個下劃線僅僅是一個有效的變量名稱呻澜,會有這個用途而已。

在下面的代碼示例中惨险,我將汽車元組拆分為單獨的變量羹幸,但我只對顏色和里程值感興趣。 但是辫愉,為了使拆分表達式成功運行栅受,我需要將包含在元組中的所有值分配給變量。 在這種情況下恭朗,“_”作為占位符變量可以派上用場:

>>>?car?=?('red',?'auto',?12,?3812.4)

>>>?color,?_,?_,?mileage?=?car


>>>?color

'red'

>>>?mileage

3812.4

>>>?_

12

除了用作臨時變量之外屏镊,“_”是大多數(shù)Python REPL中的一個特殊變量,它表示由解釋器評估的最近一個表達式的結(jié)果痰腮。

這樣就很方便了而芥,比如你可以在一個解釋器會話中訪問先前計算的結(jié)果,或者膀值,你是在動態(tài)構(gòu)建多個對象并與它們交互棍丐,無需事先給這些對象分配名字:

>>>?20?+?3

23

>>>?_

23

>>>?print(_)

23


>>>?list()

[]

>>>?_.append(1)

>>>?_.append(2)

>>>?_.append(3)

>>>?_

[1,?2,?3]

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

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

本文轉(zhuǎn)自:https://www.py.cn/faq/python/13869.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末歌逢,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子翘狱,更是在濱河造成了極大的恐慌秘案,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件潦匈,死亡現(xiàn)場離奇詭異阱高,居然都是意外死亡,警方通過查閱死者的電腦和手機茬缩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門赤惊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人寒屯,你說我怎么就攤上這事荐捻。” “怎么了寡夹?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵处面,是天一觀的道長。 經(jīng)常有香客問我菩掏,道長魂角,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任智绸,我火速辦了婚禮野揪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瞧栗。我一直安慰自己斯稳,他們只是感情好,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布迹恐。 她就那樣靜靜地躺著挣惰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪殴边。 梳的紋絲不亂的頭發(fā)上憎茂,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天,我揣著相機與錄音锤岸,去河邊找鬼竖幔。 笑死,一個胖子當著我的面吹牛是偷,可吹牛的內(nèi)容都是我干的拳氢。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼晓猛,長吁一口氣:“原來是場噩夢啊……” “哼饿幅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起戒职,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤栗恩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后洪燥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體磕秤,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年捧韵,在試婚紗的時候發(fā)現(xiàn)自己被綠了市咆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡再来,死狀恐怖蒙兰,靈堂內(nèi)的尸體忽然破棺而出磷瘤,到底是詐尸還是另有隱情,我是刑警寧澤搜变,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布采缚,位于F島的核電站,受9級特大地震影響挠他,放射性物質(zhì)發(fā)生泄漏扳抽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一殖侵、第九天 我趴在偏房一處隱蔽的房頂上張望贸呢。 院中可真熱鬧,春花似錦拢军、人聲如沸楞陷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽猜谚。三九已至,卻和暖如春赌渣,著一層夾襖步出監(jiān)牢的瞬間魏铅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工坚芜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留览芳,地道東北人。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓鸿竖,卻偏偏與公主長得像沧竟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子缚忧,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容