0徘铝、引言
不管學(xué)什么耳胎,我個人是非常喜歡小技巧(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)簡單而直觀,但可以激發(fā)你繼續(xù)深挖的興趣淹魄,你會說“原來還可以這樣做啊”郁惜。
今天就來介紹6個我最喜歡的 Trick,使用它們可以讓你的代碼更 Pythonic:
下劃線占位符
枚舉
打包
解包
動態(tài)屬性
密碼函數(shù)
它們都非常直觀而簡單甲锡,相信讀完之后兆蕉,肯定有幾個技巧會讓你驚嘆到,原來 Python 還可以這么用缤沦。
1虎韵、下劃線占位符
有時候數(shù)字一大,數(shù)起來會犯迷糊缸废,看下例包蓝。
apple_mktcap=1084000000000facebook_mktcap?=458870000000total = apple_mktcap + facebook_mktcapprint(total)
1542870000000.0
這個蘋果和臉書的市值之和有多少個零啊企量?數(shù)不清楚是吧测萎,在 Python 中,我們可以用下劃線占位符(underscore placeholder)來將大數(shù)每三位數(shù)分段届巩。請注意硅瞧,多加了下劃線,數(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
你看,加個下劃線的數(shù)字還是可以相加拇勃,但是結(jié)果還是不好認(rèn)四苇。還記得 f string 格式化字符串嗎?用 :, 來每三位數(shù)分段方咆。
print(f'Total is{total:,}USD')
Totalis1,542,870,000,000USD
“下劃線占位符”解決痛點:容易辨認(rèn)大數(shù)的位數(shù)月腋。
2、枚舉
給定一列表,包含四種計算機語言的元素榆骚。
languages= ['Python','R','Matlab','Julia']
如果我們想把每中語言附加對應(yīng)的索引一來打印出來片拍,怎么寫代碼呢?最直接的想法就是初始化 index 為 0妓肢,然后在運行每個 for 循環(huán)后將 index 的值加 1捌省,代碼如下。
index= 0for lang in languages:print(index, lang)index+=1
0Python1R2Matlab3Julia
結(jié)果是對的碉钠,但是這代碼你不覺得很丑嗎纲缓?很不 Pythonic 嗎?
Python 有 enumerate() 函數(shù)可以一次性返回列表(任意迭代器)的元素以及其對應(yīng)的索引喊废,代碼如下祝高,優(yōu)雅嗎?
forindex, lang in enumerate(languages):print(index, lang)
0Python1R2Matlab3Julia
除此之外污筷,你還可以自定義索引的初始值工闺。在實際生活中,一般索引從 1 開始更自然瓣蛀,那么將參數(shù) start 設(shè)置為 1 就好了陆蟆。
forindex, lang in enumerate(languages, start=1):print(index, lang)
1Python2R3Matlab4Julia
“枚舉函數(shù) enumerate()”解決痛點:不需要顯性創(chuàng)建索引。
3惋增、打包
給定一串名字(names)和演員角色(actors)遍搞,用兩個列表存儲。
names= ['小羅伯特唐尼','托比·馬奎爾','克里斯蒂安·貝爾','杰森·莫瑪']actors = ['鋼鐵俠','蜘蛛俠','蝙蝠俠','水行俠']
如果我們想把每個名字和角色一一對應(yīng)起來器腋,可以用上節(jié)學(xué)到的 enumerate() 函數(shù)。我們可以返回 names 里的元素和索引钩杰,再用索引來獲取 actors 里的元素纫塌,代碼如下。
forindex, nameinenumerate(names):? ? print(f'{name}是{actors[index]}')
小羅伯特唐尼是鋼鐵俠
托比·馬奎爾是蜘蛛俠
克里斯蒂安·貝爾是蝙蝠俠
杰森·莫瑪是水行俠
結(jié)果是對的讲弄,但是代碼不夠優(yōu)雅措左。來糖儡,zip() 函數(shù)了解一下线定?
forname, actorinzip(names, actors):? ? print(f'{name}是{actor}')
小羅伯特唐尼是鋼鐵俠
托比·馬奎爾是蜘蛛俠
克里斯蒂安·貝爾是蝙蝠俠
杰森·莫瑪是水行俠
zip() 函數(shù)將列表(迭代器)中對應(yīng)的元素打包成一個個元組村斟,然后返回由這些元組組成的列表饰迹。上面代碼是不是漂亮多了懈万。
再加一個列表如何榆鼠?zip() 函數(shù)表示毫無壓力踊跟。
universes= ['漫威','漫威','DC','DC']
forname, actor, universeinzip(names, actors, universes):? ? print(f'{name}是來自{universe}的{actor}')
小羅伯特唐尼是來自漫威的鋼鐵俠
托比·馬奎爾是來自漫威的蜘蛛俠
克里斯蒂安·貝爾是來自DC的蝙蝠俠
杰森·莫瑪是來自DC的水行俠
讓我們再看一次 zip() 函數(shù)的用法阁危,其?3?個參數(shù) names, actors 和 universes 列表中都有?4個元素群井,那么在對應(yīng)的位置 i(從 0 到 3)一個個獲取 names[i], actors[i] 和 universes[i]状飞,并打包成新列表,因此輸出是?4?個列表,每個列表有?3?個元素诬辈。
a= zip(names, actors, universes)print(*a)
('小羅伯特唐尼','鋼鐵俠','漫威')('托比·馬奎爾','蜘蛛俠','漫威')('克里斯蒂安·貝爾','蝙蝠俠','DC')('杰森·莫瑪','水行俠','DC')
結(jié)果沒問題酵使。需要注意的是 a 實際上是個對象,要看它里面的內(nèi)容焙糟,需要在 a 前面加個 * 字符口渔。
你們現(xiàn)在肯定會想,有了 zip()穿撮,那有沒有其反向操作的 unzip() 呢缺脉?答案是沒有,zip() 的反向操作還是 .... zip()!!!
你品混巧,你細(xì)品枪向。
a =zip(names, actors, universes)names, actors, universes =zip(*a)print(names, actors, universes)
('小羅伯特唐尼','托比·馬奎爾','克里斯蒂安·貝爾','杰森·莫瑪')('鋼鐵俠','蜘蛛俠','蝙蝠俠','水行俠')('漫威','漫威','DC','DC')
“打包函數(shù)?zip()”解決痛點:能同時遍歷多個迭代器。
4咧党、解包
一個簡單例子秘蛔,將 1 和 2 分別賦給 a 和 b,這種操作稱為解包(unpack)傍衡。
a, b =1,2print(a)print(b)
12
如果你不想要 b 的話深员,用下劃線代替就行了。
a, _ =1,2print(a)
1
但如果等號左右兩邊元素和變量個數(shù)不一樣蛙埂。程序會報錯倦畅。
a,b,c=1,2
---------------------------------------------------------------------------ValueError Traceback (most recentcalllast)in----> 1 a, b, c = 1, 2ValueError:notenoughvaluestounpack (expected3, got2)
用 * 字符可以解決這個問題。將右邊的 1 和 2 分別解包給 a 和 b绣的,那么什么都不剩了叠赐,因此 c 得到的是個空集 []。
a, b, *c=1, 2print(a)print(b)print(c)
12[]
如果右邊元素多過左邊變量呢屡江?從頭開始一一解包芭概,再把多余的全部賦給 c。
a, b, *c=1,2,3,4, 5print(a)print(b)print(c)
12[3,4,5]
更進(jìn)一步惩嘉,我們還可以從頭和尾開始一一解包罢洲,再把多余的全部賦給 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
“解包”解決痛點:將值賦給正確的變量。
5耸峭、動態(tài)屬性
這個技巧是我覺得最有用的桩蓉。首先定一個金融產(chǎn)品的類?Instrument,并創(chuàng)建一個對象 inst劳闹。
classInstrument():passinst = Instrument()
定義 inst 的兩個屬性并賦值触机,本金(notional)和到期日(maturity)帚戳。
inst.notional=100_000_000inst.maturity ='2025-03-25'
print(inst.notional)print(inst.maturity)
1000000002025-03-25
現(xiàn)在將屬性 notional 和其屬性值 10000000 存儲在變量 first_key 和 first_val 中。
first_key='notional'first_val =100_000_000
我們想用?first_key 的值 notional(而不是 first_key 這個字符)來作為屬性儡首。
inst= Instrument()inst.first_key = first_val
打印 inst.notional 會報錯片任,錯誤是 Instrument 對象中沒有 notional 這樣的屬性名。
print(inst.notional)
---------------------------------------------------------------------------AttributeError Traceback (most recentcalllast)in----> 1 print(inst.notional)AttributeError:'Instrument'objecthasnoattribute'notional'
原因是 inst 把 first_key 這個字符串當(dāng)成屬性名蔬胯,驗證如下对供。
print(inst.first_key)
100000000
怎么解決這個動態(tài)屬性的問題呢?即我們要變量的值為屬性名氛濒,而不是變量本身名稱當(dāng)屬性名产场。用 setattr() 函數(shù),它有三個參數(shù):
參數(shù) 1 - 對象
參數(shù) 2 - 屬性名的變量名
參數(shù) 3 - 屬性值的變量名
代碼如下舞竿,這時用 inst.notional 不會報錯了京景。
inst= Instrument()setattr(inst, first_key, first_val)print(inst.notional)
100000000
和 setattr() 相對應(yīng),你可以用 getattr() 函數(shù)來獲取屬性值骗奖,它有兩個參數(shù):
參數(shù) 1 - 對象
參數(shù) 2 - 屬性名的變量名
代碼如下:
getattr(inst, first_key)
100000000
和靜態(tài)屬性相比确徙,動態(tài)屬性到底好在哪里呢?以讀取歐式期權(quán)的特征舉例执桌,通常信息以字典(也有其他格式)存儲鄙皇,具體內(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 對象時,把上面字典的鍵(key)作為屬性名仰挣。每種產(chǎn)品具體的特征都不一樣伴逸,如果用靜態(tài)屬性的將字典轉(zhuǎn)成對象的話,代碼會非常亂而且無法管理膘壶,但如果用動態(tài)屬性的話错蝴,下面三行代碼就能搞定(用 setattr())。
inst= Instrument()for key, val in inst_info.items():? ? setattr(inst, key, val)
用 getattr() 函數(shù)來打印出來看結(jié)果對不對颓芭,兩行代碼搞定漱竖。
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é)果是對的,但也是丑的畜伐,用 f string 來添加若干個空白,將每個屬性值的起始位置對齊躺率。
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
“動態(tài)屬性 setattr()”解決痛點:用盡可能少的代碼快速創(chuàng)建對象玛界。
6、密碼函數(shù)
當(dāng)?shù)卿洉r悼吱,你需要輸入你的用戶名和密碼慎框,用 input() 函數(shù)可以做到要求用戶主動輸入,但是輸入的密碼任何人都可見后添,這還是密碼嗎笨枯?
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()”解決痛點:讓輸入的密碼不可見严嗜。
7、總結(jié)
六個小技巧洲敢,簡單直觀漫玄,但是超級有用。有時候就是用這樣的一個函數(shù)压彭,你不知道睦优,寫出來的代碼不優(yōu)雅;你知道了壮不,寫出來的代碼真好看汗盘。
六個技巧總結(jié)如下:
下劃線占位符:容易辨認(rèn)大數(shù)的位數(shù)
枚舉函數(shù)?enumerate():不需要顯性創(chuàng)建索引
打包函數(shù)?zip():能同時遍歷多個迭代器
解包:將值賦給正確的變量
動態(tài)屬性?setattr():用盡可能少的代碼快速創(chuàng)建對象
密碼函數(shù)?getpass():讓輸入的密碼不可見
用起來,酷起來询一。