Python中解包操作的性能實(shí)測與最佳實(shí)踐
“說真的,我寫Python這么多年了,直到某天被一個(gè)看似無聊的bug坑了兩個(gè)小時(shí),我才真正意識到:解包操作這個(gè)小東西,背后藏著的門道,是真不少。”
今天咱就一起來聊聊,Python 的解包操作,那些你以為你懂了,但其實(shí)還差點(diǎn)火候的細(xì)節(jié)。
什么是解包
先別急著劃走,這不是你想的 “哦!我早會了” 的那種。
來個(gè)簡單例子熱熱身:
a, b = [1, 2] print(a) # 1 print(b) # 2
這就是最常見的解包操作。Python把右邊的列表拆開,把值分別賦給左邊的變量。
但是!你以為就這點(diǎn)花活?那可就太小看解包了。
多層解包:不只是解開“表層”
有一次我在處理一個(gè)嵌套的數(shù)據(jù)結(jié)構(gòu),寫著寫著就頭暈了,然后突然靈光一閃:
data = ("花姐", (28, "Python博主")) name, (age, title) = data print(name) # 花姐 print(age) # 28 print(title) # Python博主
這其實(shí)是多層解包,一行搞定,專業(yè)又不失優(yōu)雅。
但是如果你還在用下標(biāo)訪問,那未免有點(diǎn)費(fèi)勁:
name = data[0] age = data[1][0] title = data[1][1]
這不是寫代碼,這是折騰自己。
星號 * 解包:別小看這個(gè)“星號”
很多人知道它可以用在函數(shù)參數(shù)上,比如:
def greet(*args): for arg in args: print(f"Hi {arg}") greet("花姐", "讀者朋友", "小黑貓")
但你可能沒注意,它還可以用在變量賦值上:
first, *middle, last = [1, 2, 3, 4, 5] print(first) # 1 print(middle) # [2, 3, 4] print(last) # 5
這個(gè)在處理不定長度的序列時(shí)特別爽,用過的都說 “真香” 。
解包操作的性能測試
技術(shù)博主嘛,不測性能總感覺文章不夠硬。
我搞了一個(gè)小測試:
import time lst = list(range(1000000)) # 解包 start = time.time() a, *b = lst end = time.time() print("解包耗時(shí):", end - start) # 切片 start = time.time() a = lst[0] b = lst[1:] end = time.time() print("切片耗時(shí):", end - start)
運(yùn)行結(jié)果:
解包耗時(shí): 0.007996559143066406
切片耗時(shí): 0.01676344871520996
這時(shí)候很多人就會下結(jié)論說:“誒?那看來解包還比切片快呀!”
但,先別急,這結(jié)論下得太早了,就像看到某人微信頭像是貓,就說他肯定擼貓達(dá)人,說不定人家只是懶得換頭像罷了。
為什么感覺“切片比解包慢”?其實(shí)你被假象騙了!
前面我們做了一個(gè)性能測試,結(jié)果顯示解包更快
但先別急著站隊(duì),咱得搞清楚——這兩個(gè)操作本質(zhì)上干的事情就不一樣!
來看對比代碼:
# 解包 a, *b = lst # 把第一個(gè)元素單獨(dú)取出來,其余自動打包成列表 b # 切片 a = lst[0] b = lst[1:] # 拿出第一個(gè)元素 + 手動切片構(gòu)建新列表
雖然表面看是一樣的結(jié)果,但背后差了不少事兒:
操作 | 背后干了什么 |
---|---|
解包 | 解釋器做了優(yōu)化,生成新列表的過程相對高效 |
切片 | .copy() 了一個(gè)子列表,需要分配內(nèi)存、復(fù)制元素等操作 |
所以,解包有點(diǎn)像“偷偷快了一點(diǎn)”,但不是它真有多猛,而是Python幫它抄了點(diǎn)近路。
更嚴(yán)謹(jǐn)?shù)臏y試方式推薦
剛才我們用 time.time()
粗測一輪其實(shí)參考價(jià)值有限,來,用 timeit
試試更科學(xué)的方式:
import timeit setup = "lst = list(range(1000000))" print("解包耗時(shí):", timeit.timeit("a, *b = lst", setup=setup, number=10)) print("切片耗時(shí):", timeit.timeit("a = lst[0]; b = lst[1:]", setup=setup, number=10))
這個(gè)測試你多跑幾次,會發(fā)現(xiàn):
- 解包不一定總快,有時(shí)候差距其實(shí)很小
- 數(shù)據(jù)量越大,切片復(fù)制帶來的開銷越明顯
小結(jié)一下:
解包 vs 切片,不是誰“性能更強(qiáng)”,而是看你“用在什么地方”。
1.用解包:
- 要快速拆第一項(xiàng)或最后一項(xiàng)
- 不需要太精確地控制中間部分
- 更喜歡語法糖的簡潔
2.用切片:
- 需要處理特定區(qū)間的數(shù)據(jù)
- 對性能特別敏感時(shí)
- 數(shù)據(jù)結(jié)構(gòu)復(fù)雜,解包可讀性反而下降時(shí)
一句話總結(jié):
解包適合“拿一點(diǎn)留一堆”,切片適合“精準(zhǔn)控制拿哪段”。
別被表面的耗時(shí)騙了,就像聽說隔壁老王做副業(yè)月入十萬,你不能直接辭職——得搞清楚人家到底干了啥
易被忽略的坑點(diǎn)
小貼士時(shí)間到了,以下這些細(xì)節(jié),真的是很多Python開發(fā)者都會忽略的:
1. 解包不能比右邊變量多
a, b = [1] # ValueError: not enough values to unpack (expected 2, got 1)
這個(gè)報(bào)錯誰沒遇到過?我第一次見到它的時(shí)候還以為是編輯器出bug了,重啟了IDE才發(fā)現(xiàn)是自己菜
2. 解包對象必須是可迭代的
a, b = None # TypeError: cannot unpack non-iterable NoneType object
別問我怎么知道的,那天debug一個(gè)空返回值的時(shí)候差點(diǎn)把鍵盤砸了。
3. 字典解包的順序不是你想的那樣
a, b = {'x': 1, 'y': 2} print(a, b) # x y
你以為解的是值,其實(shí)解的是 key。要值?得 .values()
啊哥:
a, b = {'x': 1, 'y': 2}.values() print(a, b) # 1 2
實(shí)戰(zhàn)場景:解包的實(shí)際用法
有次我寫爬蟲,需要從元組列表中提取字段:
data = [ ("Python", 95), ("Java", 85), ("Go", 75) ] for lang, score in data: print(f"{lang} 的得分是 {score}")
這要是你還在手動 data[i][j]
,那你可能真的沒理解Python的“優(yōu)雅”。
再比如交換兩個(gè)變量的值,常規(guī)寫法要用臨時(shí)變量對吧:
temp = a a = b b = temp
Python直接來個(gè):
a, b = b, a
看到這里,連我那只平時(shí)只知道趴在路由器上取暖的貓都叫了一聲“妙啊”。
解包 + 函數(shù)參數(shù) = 神操作
你可以用 *
和 **
解包參數(shù):
def show(name, age): print(f"{name} - {age}") args = ("花姐", 28) show(*args)
字典也行:
kwargs = {"name": "花姐", "age": 28} show(**kwargs)
這玩意配合 map()
、多線程、協(xié)程操作,能玩出花來。下次再細(xì)說,不然你得說我扯太多了。
總結(jié)一下
說實(shí)話,解包這種東西,一開始學(xué)Python時(shí)覺得是“語法糖”,后來真香打臉。
它不只是“方便”,更多時(shí)候能幫你寫出更清晰、可讀性更強(qiáng)的代碼。但別忘了性能問題和邊界條件,不然“糖吃多了也上火”。
到此這篇關(guān)于Python中解包操作的性能實(shí)測與最佳實(shí)踐的文章就介紹到這了,更多相關(guān)Python解包操作內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
pytorch之深度神經(jīng)網(wǎng)絡(luò)概念全面整理
這篇文章主要介紹了pytorch之深度神經(jīng)網(wǎng)絡(luò)概念,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09解決django同步數(shù)據(jù)庫的時(shí)候app models表沒有成功創(chuàng)建的問題
今天小編就為大家分享一篇解決django同步數(shù)據(jù)庫的時(shí)候app models表沒有成功創(chuàng)建的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08對python實(shí)現(xiàn)二維函數(shù)高次擬合的示例詳解
今天小編就為大家分享一篇對python實(shí)現(xiàn)二維函數(shù)高次擬合的示例詳解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-12-12python變量數(shù)據(jù)類型和運(yùn)算符
這篇文章主要介紹了python變量數(shù)據(jù)類型和運(yùn)算符,不同類型的變量可以進(jìn)行的運(yùn)算是不同的,所以必須理解變量的類型,下面文章的更多相關(guān)內(nèi)容介紹,需要的小伙伴可以參考一下2022-07-07如何使用python編寫一個(gè)簡單的課時(shí)記錄系統(tǒng)
編寫一個(gè)應(yīng)用系統(tǒng)需要多方面的知識和技能,下面這篇文章主要給大家介紹了關(guān)于如何使用python編寫一個(gè)簡單的課時(shí)記錄系統(tǒng)的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用python具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-04-04keras load model時(shí)出現(xiàn)Missing Layer錯誤的解決方式
這篇文章主要介紹了keras load model時(shí)出現(xiàn)Missing Layer錯誤的解決方式,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-06-06