關(guān)于Python的前后、單雙下劃線作用,看完這篇文章,吊打面試官!
點擊上方“AI算法與圖像處理”,選擇加"星標"或“置頂”
重磅干貨,第一時間送達
來源:清風python
在Python中,可能最常見的就是各種常量、變量、函數(shù)、方法前后添加的那些下劃線了。有前面加的、后面加的,加一個的,加兩個的,看到頭暈。那么,你對這些知識都掌握了嗎 ?讓我們先來做一個自測吧。
題目:說明以下四個例子輸出的結(jié)果分別是什么。

各位,請開始你的表演,來看看以上4段代碼分別輸出的結(jié)果是什么?OK,記住你的答案,等看完文章解開謎底后,再來看看的答對了沒。
單前導(dǎo)下劃線(_xxx),作為Python的命名約定,表示僅供內(nèi)部使用。但注意這個命名約定,在類中你使用單前導(dǎo)線聲明的變量,**依然可以在外部直接訪問。**那這種命名約定還有什么意義呢?有!當代碼使用
from modlue import *?
導(dǎo)入某個模塊時,單前導(dǎo)線這種定義方式的屬性,不會被導(dǎo)入。舉例:
#?demo1.py
Name?=?"清風"
_Age?=?18
#?demo2.py
from?demo1?import?*
print(print(Name,_Age))
#output:
NameError:?name?'_Age'?is?not?defined??
正常的情況是如上結(jié)果,但是萬事無絕對,面試官的陰人考點來了:
__all__ = ["Name", "_Age"]
當demo1.py在開頭聲明如上,使用__all__單獨聲明了可導(dǎo)入內(nèi)容時,可以正常導(dǎo)入。雖然使用起來矛盾,但是面試陰人必備有木有?
單末尾下劃線(xxx_),按照PEP8規(guī)定,單末尾下劃線也是一個約定 用來避免與python關(guān)鍵字產(chǎn)生命名沖突。
例如:我們使用Beautifulsoup進行網(wǎng)頁解析,通過類方法定位時,會找某個標簽它的存在class=‘xxx’的情況,此時css的class與Python中的類重名,需要在class后添加單下劃線進行區(qū)分。類似場景還有很多,就不一一列舉了。
日常開發(fā)中,最好避免在自己的程序中使用以雙下劃線(“dunders”)開頭和結(jié)尾的名稱,因為它是Python語言定義的一種特殊方法(魔法方法),我們熟知的__init__ 、__dict__ 、__getitem__等等。
但是,如果你非要使用這種寫法去聲明,那可真是無底坑...如果你聲明的變量不是內(nèi)置的魔法方法,Python會將它當做普通的變量來操作。如果和內(nèi)置的方法重名,要么重寫,要么因為功能沖突而引發(fā)報錯,所以不作死就不會死,還是別這么玩了。
這個為什么放在最后,因為壓軸啊!雙前導(dǎo)下劃線,在面試中被考到的幾率太大了,尤其是那種長相猥瑣,心術(shù)不正的面試官,最愛問這個知識點,所以要牢記。
首先雙前導(dǎo)下劃線(__xxx)的命名,90%情況下是真切的私有變量、方法,剩下10%一會兒再說。下來說說雙前導(dǎo)下劃線的作用,既然為私有屬性,那么僅在當前類中可用,外部、子類均無法調(diào)用和繼承。知道這點寫代碼差不多夠了,但還差一點,拿文章開頭的最后一個例子來說
#?Test4
class?Root:
????def?__func(self):
????????print('root')
class?Child(Root):
????def?__init__(self):
????????self.__func()
Child()
大家剛才的答案是什么,root?恭喜你,打錯了,結(jié)果是:
AttributeError: 'Child' object has no attribute '_Child__func'
不該是子類沒有的方法,繼承父類么,明明父類有,為什么會報錯。剛才我們說到了,雙前導(dǎo)下劃線是真切的私有變量、方法,無法被子類所繼承。如果我們把雙前導(dǎo)下劃線,變成了單前導(dǎo)下劃線(如Test3),那么結(jié)果是root。
不能繼承的問題明白了,但這個_Child__func是什么鬼?這就要說為什么剛才我說雙前導(dǎo)下劃線90%的情況下是真切的私有變量了、讓我們來看下面的例子:
class?Demo:
????def?__init__(self):
????????self.__name?=?"清風Python"
????def?__say_hello(self):
????????print(f"你好:{self.__name}")
????????
D?=?Demo()
#?usually
print(D.__name)
D.__say_hello
#?specially
print(D._Demo__name)
D._Demo__say_hello()
我們定義一個Demo類,其中存在雙前導(dǎo)下劃線的__name __say_hello,當我們使用通常的調(diào)用方式時,是無法執(zhí)行的,但Python的私有屬性聲明時,其實就是將某個私有屬性前添加單下劃線+類名,所以如果其實不存在什么私有屬性,我們可以通過_classname__privatefunc的方式實現(xiàn)強制調(diào)用。
那么,日常中我們能否盡量的避免這種方式呢?方法是有的,但是只能騙騙初學者,對有心人還是沒用,比如:
class?Demo:
????def?__init__(self):
????????self.__money?=?100
????@property
????def?money(self):
????????return?self.__money
????
[email protected]
????def?money(self,?pwd):
????????pass
????
D?=?Demo()
D.money?=?1000000
print(D.money)?#?依舊為100塊
python的property裝飾器,可以將方法聲明為類的屬性,當某人調(diào)用D.money得到自己余額為100塊時,肯定想著我重新賦值余額秒變土豪,但真實的余額我們使用的是私有的self.__money。而通過property創(chuàng)建了一個money的屬性,當用戶對money賦值時,money.setter的方法是空的,你怎么賦值都是無用的(空的干嘛還要寫,因為不寫會報錯啊...AttributeError: can't set attribute)。
這樣看起來很完美啊,為什么說只能騙騙初學者?當你打印print(D.__dict__)時{'_Demo__money': 100}一目了然。
最后,文章開頭的測試題答案你做對了么?結(jié)果是:
child、root、root、報錯??你答對了么?
今天關(guān)于Python中下劃線的內(nèi)容就到此為止,是否起到了穩(wěn)固執(zhí)行的效果呢?如果覺得有所收獲,歡迎分享給你的小伙伴們一起進步啊!
END
下載1:動手學深度學習
在「AI算法與圖像處理」公眾號后臺回復(fù):OpenCV黑魔法,即可下載小編精心編寫整理的計算機視覺趣味實戰(zhàn)教程
下載2 在「AI算法與圖像處理」公眾號后臺回復(fù):CVPR2020,即可下載1467篇CVPR?2020論文 個人微信(如果沒有備注不拉群!) 請注明:地區(qū)+學校/企業(yè)+研究方向+昵稱


