6個(gè)Python超酷技巧,原來(lái)還能這樣用!初學(xué)必看

0、引言

不管學(xué)什么,我個(gè)人是非常喜歡小技巧(tricks)的,Python 也不例外。著名 Python 技巧大師 Dan Bader 是這樣定義 Python Tricks 的。

A Python Trick either teaches an aspect of Python with a simple illustration, or it serves as a motivating example, enabling you to dig deeper and develop an intuitive understanding.

Dan Bader

Python Trick 表現(xiàn)簡(jiǎn)單而直觀,但可以激發(fā)你繼續(xù)深挖的興趣,你會(huì)說(shuō)“原來(lái)還可以這樣做啊”。

今天就來(lái)介紹6個(gè)我最喜歡的 Trick,使用它們可以讓你的代碼更 Pythonic:

下劃線占位符

枚舉

打包

解包

動(dòng)態(tài)屬性

密碼函數(shù)

它們都非常直觀而簡(jiǎn)單,相信讀完之后,肯定有幾個(gè)技巧會(huì)讓你驚嘆到,原來(lái) Python 還可以這么用。

1、下劃線占位符

有時(shí)候數(shù)字一大,數(shù)起來(lái)會(huì)犯迷糊,看下例。

apple_mktcap=1084000000000facebook_mktcap?=458870000000total = apple_mktcap + facebook_mktcapprint(total)

1542870000000.0

這個(gè)蘋(píng)果和臉書(shū)的市值之和有多少個(gè)零?。繑?shù)不清楚是吧,在 Python 中,我們可以用下劃線占位符(underscore placeholder)來(lái)將大數(shù)每三位數(shù)分段。請(qǐng)注意,多加了下劃線,數(shù)字還是數(shù)值型變量,只是讓我們?nèi)菀妆嬲J(rèn)大數(shù)。

apple_mktcap=1_084_000_000_000facebook_mktcap =458_870_000_000total = apple_mktcap + facebook_mktcapprint(total)

1542870000000

你看,加個(gè)下劃線的數(shù)字還是可以相加,但是結(jié)果還是不好認(rèn)。還記得 f string 格式化字符串嗎?用 :, 來(lái)每三位數(shù)分段。

print(f'Total is{total:,}USD')

Totalis1,542,870,000,000USD

“下劃線占位符”解決痛點(diǎn):容易辨認(rèn)大數(shù)的位數(shù)。

2、枚舉

給定一列表,包含四種計(jì)算機(jī)語(yǔ)言的元素。

languages= ['Python','R','Matlab','Julia']

如果我們想把每中語(yǔ)言附加對(duì)應(yīng)的索引一來(lái)打印出來(lái),怎么寫(xiě)代碼呢?最直接的想法就是初始化 index 為 0,然后在運(yùn)行每個(gè) for 循環(huán)后將 index 的值加 1,代碼如下。

index= 0for lang in languages:print(index, lang)index+=1

0Python1R2Matlab3Julia

結(jié)果是對(duì)的,但是這代碼你不覺(jué)得很丑嗎?很不 Pythonic 嗎?

Python 有 enumerate() 函數(shù)可以一次性返回列表(任意迭代器)的元素以及其對(duì)應(yīng)的索引,代碼如下,優(yōu)雅嗎?

forindex, lang in enumerate(languages):print(index, lang)

0Python1R2Matlab3Julia

除此之外,你還可以自定義索引的初始值。在實(shí)際生活中,一般索引從 1 開(kāi)始更自然,那么將參數(shù) start 設(shè)置為 1 就好了。

forindex, lang in enumerate(languages, start=1):print(index, lang)

1Python2R3Matlab4Julia

“枚舉函數(shù) enumerate()”解決痛點(diǎn):不需要顯性創(chuàng)建索引。

3、打包

給定一串名字(names)和演員角色(actors),用兩個(gè)列表存儲(chǔ)。

names= ['小羅伯特唐尼','托比·馬奎爾','克里斯蒂安·貝爾','杰森·莫瑪']actors = ['鋼鐵俠','蜘蛛俠','蝙蝠俠','水行俠']

如果我們想把每個(gè)名字和角色一一對(duì)應(yīng)起來(lái),可以用上節(jié)學(xué)到的 enumerate() 函數(shù)。我們可以返回 names 里的元素和索引,再用索引來(lái)獲取 actors 里的元素,代碼如下。

forindex, nameinenumerate(names):? ? print(f'{name}是{actors[index]}')

小羅伯特唐尼是鋼鐵俠

托比·馬奎爾是蜘蛛俠

克里斯蒂安·貝爾是蝙蝠俠

杰森·莫瑪是水行俠

結(jié)果是對(duì)的,但是代碼不夠優(yōu)雅。來(lái),zip() 函數(shù)了解一下?

forname, actorinzip(names, actors):? ? print(f'{name}是{actor}')

小羅伯特唐尼是鋼鐵俠

托比·馬奎爾是蜘蛛俠

克里斯蒂安·貝爾是蝙蝠俠

杰森·莫瑪是水行俠

zip() 函數(shù)將列表(迭代器)中對(duì)應(yīng)的元素打包成一個(gè)個(gè)元組,然后返回由這些元組組成的列表。上面代碼是不是漂亮多了。

再加一個(gè)列表如何?zip() 函數(shù)表示毫無(wú)壓力。

universes= ['漫威','漫威','DC','DC']

forname, actor, universeinzip(names, actors, universes):? ? print(f'{name}是來(lái)自{universe}的{actor}')

小羅伯特唐尼是來(lái)自漫威的鋼鐵俠

托比·馬奎爾是來(lái)自漫威的蜘蛛俠

克里斯蒂安·貝爾是來(lái)自DC的蝙蝠俠

杰森·莫瑪是來(lái)自DC的水行俠

讓我們?cè)倏匆淮?zip() 函數(shù)的用法,其?3?個(gè)參數(shù) names, actors 和 universes 列表中都有?4個(gè)元素,那么在對(duì)應(yīng)的位置 i(從 0 到 3)一個(gè)個(gè)獲取 names[i], actors[i] 和 universes[i],并打包成新列表,因此輸出是?4?個(gè)列表,每個(gè)列表有?3?個(gè)元素。

a= zip(names, actors, universes)print(*a)

('小羅伯特唐尼','鋼鐵俠','漫威')('托比·馬奎爾','蜘蛛俠','漫威')('克里斯蒂安·貝爾','蝙蝠俠','DC')('杰森·莫瑪','水行俠','DC')

結(jié)果沒(méi)問(wèn)題。需要注意的是 a 實(shí)際上是個(gè)對(duì)象,要看它里面的內(nèi)容,需要在 a 前面加個(gè) * 字符。

你們現(xiàn)在肯定會(huì)想,有了 zip(),那有沒(méi)有其反向操作的 unzip() 呢?答案是沒(méi)有,zip() 的反向操作還是 .... zip()!!!

你品,你細(xì)品。

a =zip(names, actors, universes)names, actors, universes =zip(*a)print(names, actors, universes)

('小羅伯特唐尼','托比·馬奎爾','克里斯蒂安·貝爾','杰森·莫瑪')('鋼鐵俠','蜘蛛俠','蝙蝠俠','水行俠')('漫威','漫威','DC','DC')

“打包函數(shù)?zip()”解決痛點(diǎn):能同時(shí)遍歷多個(gè)迭代器。

4、解包

一個(gè)簡(jiǎn)單例子,將 1 和 2 分別賦給 a 和 b,這種操作稱為解包(unpack)。

a, b =1,2print(a)print(b)

12

如果你不想要 b 的話,用下劃線代替就行了。

a, _ =1,2print(a)

1

但如果等號(hào)左右兩邊元素和變量個(gè)數(shù)不一樣。程序會(huì)報(bào)錯(cuò)。

a,b,c=1,2

---------------------------------------------------------------------------ValueError Traceback (most recentcalllast)in----> 1 a, b, c = 1, 2ValueError:notenoughvaluestounpack (expected3, got2)

用 * 字符可以解決這個(gè)問(wèn)題。將右邊的 1 和 2 分別解包給 a 和 b,那么什么都不剩了,因此 c 得到的是個(gè)空集 []。

a, b, *c=1, 2print(a)print(b)print(c)

12[]

如果右邊元素多過(guò)左邊變量呢?從頭開(kāi)始一一解包,再把多余的全部賦給 c。

a, b, *c=1,2,3,4, 5print(a)print(b)print(c)

12[3,4,5]

更進(jìn)一步,我們還可以從頭和尾開(kāi)始一一解包,再把多余的全部賦給 c。

a, b, *c, d =1,2,3,4, 5print(a)print(b)print(c)print(d)

12[3,4]5

不想要 c 的話,用 *_ 將其代替即可。

a,b,*_,d=1,2,3,4,5print(a)print(b)print(d)

125

“解包”解決痛點(diǎn):將值賦給正確的變量。

5、動(dòng)態(tài)屬性

這個(gè)技巧是我覺(jué)得最有用的。首先定一個(gè)金融產(chǎn)品的類?Instrument,并創(chuàng)建一個(gè)對(duì)象 inst。

classInstrument():passinst = Instrument()

定義 inst 的兩個(gè)屬性并賦值,本金(notional)和到期日(maturity)。

inst.notional=100_000_000inst.maturity ='2025-03-25'

print(inst.notional)print(inst.maturity)

1000000002025-03-25

現(xiàn)在將屬性 notional 和其屬性值 10000000 存儲(chǔ)在變量 first_key 和 first_val 中。

first_key='notional'first_val =100_000_000

我們想用?first_key 的值 notional(而不是 first_key 這個(gè)字符)來(lái)作為屬性。

inst= Instrument()inst.first_key = first_val

打印 inst.notional 會(huì)報(bào)錯(cuò),錯(cuò)誤是 Instrument 對(duì)象中沒(méi)有 notional 這樣的屬性名。

print(inst.notional)

---------------------------------------------------------------------------AttributeError Traceback (most recentcalllast)in----> 1 print(inst.notional)AttributeError:'Instrument'objecthasnoattribute'notional'

原因是 inst 把 first_key 這個(gè)字符串當(dāng)成屬性名,驗(yàn)證如下。

print(inst.first_key)

100000000

怎么解決這個(gè)動(dòng)態(tài)屬性的問(wèn)題呢?即我們要變量的值為屬性名,而不是變量本身名稱當(dāng)屬性名。用 setattr() 函數(shù),它有三個(gè)參數(shù):

參數(shù) 1 - 對(duì)象

參數(shù) 2 - 屬性名的變量名

參數(shù) 3 - 屬性值的變量名

代碼如下,這時(shí)用 inst.notional 不會(huì)報(bào)錯(cuò)了。

inst= Instrument()setattr(inst, first_key, first_val)print(inst.notional)

100000000

和 setattr() 相對(duì)應(yīng),你可以用 getattr() 函數(shù)來(lái)獲取屬性值,它有兩個(gè)參數(shù):

參數(shù) 1 - 對(duì)象

參數(shù) 2 - 屬性名的變量名

代碼如下:

getattr(inst, first_key)

100000000

和靜態(tài)屬性相比,動(dòng)態(tài)屬性到底好在哪里呢?以讀取歐式期權(quán)的特征舉例,通常信息以字典(也有其他格式)存儲(chǔ),具體內(nèi)容如下:

inst_info = {'ID':'9001001','EffectiveDate':'2020-03-20','MaturityDate':'2020-06-20','Notional':10_000_000,'DomesticCurrency':'USD','ForeignCurrency':'EUR','Flavor':'Put','Strike':1.08,'Display':'domestic?pips','AssetClass':'FX','Instrument?Type':'EuropeanOption','Model':'Heston'}

那么當(dāng)我們創(chuàng)建 inst 對(duì)象時(shí),把上面字典的鍵(key)作為屬性名。每種產(chǎn)品具體的特征都不一樣,如果用靜態(tài)屬性的將字典轉(zhuǎn)成對(duì)象的話,代碼會(huì)非常亂而且無(wú)法管理,但如果用動(dòng)態(tài)屬性的話,下面三行代碼就能搞定(用 setattr())。

inst= Instrument()for key, val in inst_info.items():? ? setattr(inst, key, val)

用 getattr() 函數(shù)來(lái)打印出來(lái)看結(jié)果對(duì)不對(duì),兩行代碼搞定。

forkeyininst_info.keys():print( key,'|', getattr(inst, key))

ID?|9001001EffectiveDate |?2020-03-20MaturityDate?| 2020-06-20Notional|?10000000DomesticCurrency?| USDForeignCurrency |?EURFlavor?|PutStrike|?1.08Display?|domestic pipsAssetClass |?FXInstrumentType?| European OptionModel|?Heston

結(jié)果是對(duì)的,但也是丑的,用 f string 來(lái)添加若干個(gè)空白,將每個(gè)屬性值的起始位置對(duì)齊。

forkeyininst_info.keys():print( f'{key:18s}|', getattr(inst, key))

ID?|9001001EffectiveDate |?2020-03-20MaturityDate?| 2020-06-20Notional|?10000000DomesticCurrency?| USDForeignCurrency |?EURFlavor?|PutStrike|?1.08Display?|domestic pipsAssetClass |?FXInstrumentType?| European OptionModel|?Heston

“動(dòng)態(tài)屬性 setattr()”解決痛點(diǎn):用盡可能少的代碼快速創(chuàng)建對(duì)象。

6、密碼函數(shù)

當(dāng)?shù)卿洉r(shí),你需要輸入你的用戶名和密碼,用 input() 函數(shù)可以做到要求用戶主動(dòng)輸入,但是輸入的密碼任何人都可見(jiàn),這還是密碼嗎?

username =input('Username: ')password =input('Password: ')print('Logging In...')

Username:StevenPassword:?1031LoggingIn ...

用 getpass() 函數(shù)即可,不解釋,自己看下圖。

fromgetpassimportgetpassusername = input('Username: ')password = getpass('Password: ')print('Logging In...')

Username:StevenPassword:········LoggingIn ...

“密碼函數(shù) getpass()”解決痛點(diǎn):讓輸入的密碼不可見(jiàn)。

7、總結(jié)

六個(gè)小技巧,簡(jiǎn)單直觀,但是超級(jí)有用。有時(shí)候就是用這樣的一個(gè)函數(shù),你不知道,寫(xiě)出來(lái)的代碼不優(yōu)雅;你知道了,寫(xiě)出來(lái)的代碼真好看。

六個(gè)技巧總結(jié)如下:

下劃線占位符:容易辨認(rèn)大數(shù)的位數(shù)

枚舉函數(shù)?enumerate():不需要顯性創(chuàng)建索引

打包函數(shù)?zip():能同時(shí)遍歷多個(gè)迭代器

解包:將值賦給正確的變量

動(dòng)態(tài)屬性?setattr():用盡可能少的代碼快速創(chuàng)建對(duì)象

密碼函數(shù)?getpass():讓輸入的密碼不可見(jiàn)

用起來(lái),酷起來(lái)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容