Python 多變量賦值問題小結(jié)
今天在刷鏈表反轉(zhuǎn)題的時(shí)候,遇到了兩段看起來幾乎一樣的代碼,結(jié)果一個(gè)能正確反轉(zhuǎn)鏈表,一個(gè)卻不行。仔細(xì)研究后發(fā)現(xiàn),問題出在 Python 的多變量賦值順序上 —— 這個(gè)平時(shí)沒太在意的細(xì)節(jié),在操作鏈表指針時(shí)居然能造成這么大的影響。記錄一下我的分析過程,免得以后再踩類似的坑。
問題背景:兩段 “相似” 的反轉(zhuǎn)代碼
先看這兩段代碼,都是用迭代法反轉(zhuǎn)單鏈表:
代碼 1(能正確反轉(zhuǎn)):
def reverseList(head):
last = None
while head:
# 關(guān)鍵賦值行
last, head.next, head = head, last, head.next
return last
代碼 2(無法正確反轉(zhuǎn)):
def reverseList(head):
last = None
while head:
# 關(guān)鍵賦值行(順序不同)
head, head.next, last = head.next, last, head
return last
第一眼看上去,只是左側(cè)變量的賦值順序變了,右側(cè)的值好像還是那幾個(gè)。但實(shí)際運(yùn)行時(shí),代碼 1 能得到正確的反轉(zhuǎn)鏈表,代碼 2 卻會(huì)返回錯(cuò)誤結(jié)果(比如鏈表斷裂、部分節(jié)點(diǎn)丟失)。這到底是為什么?
核心原因:Python 多變量賦值的 “秘密”
在 Python 里,多變量賦值的執(zhí)行邏輯是 “先計(jì)算右側(cè)所有表達(dá)式的值,再一次性賦值給左側(cè)變量”。也就是說,右側(cè)的值都是基于賦值前的原始狀態(tài)計(jì)算的,不會(huì)被左側(cè)的中間賦值影響。
但這一點(diǎn),需要注意的是 “=”右側(cè)是獲取值,所以可以理解為完全并行執(zhí)行的。但是左側(cè)是改變值,這就意味著是會(huì)順序影響
逐行分析:兩段代碼的執(zhí)行差異
我們用一個(gè)簡(jiǎn)單的鏈表1->2->3來模擬執(zhí)行過程,看看兩段代碼的區(qū)別。
代碼 1 的執(zhí)行邏輯(正確)
關(guān)鍵賦值行:last, head.next, head = head, last, head.next
拆解步驟(每次循環(huán)):
- 先算右側(cè)值(基于當(dāng)前原始狀態(tài)):
- 第一個(gè)值:
head(當(dāng)前節(jié)點(diǎn),比如第一輪是 1) - 第二個(gè)值:
last(上一個(gè)節(jié)點(diǎn),初始是 None) - 第三個(gè)值:
head.next(下一個(gè)節(jié)點(diǎn),第一輪是 2)
- 再給左側(cè)賦值:
last= 第一個(gè)值(當(dāng)前節(jié)點(diǎn) 1)→ 現(xiàn)在 last 指向 1head.next= 第二個(gè)值(原 last,即 None)→ 節(jié)點(diǎn) 1 的 next 指向 None(完成反轉(zhuǎn)第一步)head= 第三個(gè)值(下一個(gè)節(jié)點(diǎn) 2)→ 移動(dòng) head 到下一個(gè)節(jié)點(diǎn),繼續(xù)循環(huán)
整個(gè)過程中,“反轉(zhuǎn)當(dāng)前節(jié)點(diǎn)指針” 和 “移動(dòng) head 到下一個(gè)節(jié)點(diǎn)” 是基于原始值操作的,互不干擾。比如不會(huì)因?yàn)?head 移動(dòng)了,導(dǎo)致當(dāng)前節(jié)點(diǎn)的指針沒反轉(zhuǎn)。
代碼 2 的執(zhí)行邏輯(錯(cuò)誤)
關(guān)鍵賦值行:head, head.next, last = head.next, last, head
同樣用1->2->3模擬:
- 先算右側(cè)值(基于當(dāng)前原始狀態(tài)):
- 第一個(gè)值:
head.next(下一個(gè)節(jié)點(diǎn) 2) - 第二個(gè)值:
last(初始 None) - 第三個(gè)值:
head(當(dāng)前節(jié)點(diǎn) 1)
- 再給左側(cè)賦值:
head= 第一個(gè)值(下一個(gè)節(jié)點(diǎn) 2)→ 此時(shí) head 已經(jīng)指向 2 了head.next= 第二個(gè)值(原 last,即 None)→ 注意!這里的 head 已經(jīng)是 2 了,所以實(shí)際是把節(jié)點(diǎn) 2 的 next 改成了 Nonelast= 第三個(gè)值(原 head,即節(jié)點(diǎn) 1)→ last 指向 1
這就出問題了:原本應(yīng)該反轉(zhuǎn) “當(dāng)前節(jié)點(diǎn) 1 的指針”,結(jié)果因?yàn)橄纫苿?dòng)了 head 到 2,導(dǎo)致實(shí)際修改的是 “節(jié)點(diǎn) 2 的指針”。節(jié)點(diǎn) 1 的指針根本沒反轉(zhuǎn),后續(xù)循環(huán)也再也碰不到節(jié)點(diǎn) 1 了,鏈表直接從 1 這里斷了。
到此這篇關(guān)于Python 多變量賦值問題的文章就介紹到這了,更多相關(guān)Python 多變量賦值內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python實(shí)現(xiàn)Excel和TXT文本之間相互轉(zhuǎn)換
Excel是一種具有強(qiáng)大的數(shù)據(jù)處理和圖表制作功能的電子表格文件,而TXT則是一種簡(jiǎn)單通用、易于編輯的純文本文件,本文將介紹如何使用Python并結(jié)合相關(guān)庫(kù)來實(shí)現(xiàn) Excel 和 TXT 文本文件之間的相互轉(zhuǎn)換,需要的朋友可以參考下2024-06-06
Python如何實(shí)現(xiàn)拆分?jǐn)?shù)據(jù)集
這篇文章主要介紹了Python如何實(shí)現(xiàn)拆分?jǐn)?shù)據(jù)集問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09
python celery分布式任務(wù)隊(duì)列的使用詳解
這篇文章主要介紹了python celery分布式任務(wù)隊(duì)列的使用詳解,Celery 是一個(gè) 基于python開發(fā)的分布式異步消息任務(wù)隊(duì)列,通過它可以輕松的實(shí)現(xiàn)任務(wù)的異步處理, 如果你的業(yè)務(wù)場(chǎng)景中需要用到異步任務(wù),就可以考慮使用celery,需要的朋友可以參考下2019-07-07
python getpass實(shí)現(xiàn)密文實(shí)例詳解
這篇文章主要介紹了python getpass實(shí)現(xiàn)密文實(shí)例詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
Python cookbook(數(shù)據(jù)結(jié)構(gòu)與算法)實(shí)現(xiàn)對(duì)不原生支持比較操作的對(duì)象排序算法示例
這篇文章主要介紹了Python cookbook(數(shù)據(jù)結(jié)構(gòu)與算法)實(shí)現(xiàn)對(duì)不原生支持比較操作的對(duì)象排序算法,結(jié)合實(shí)例形式分析了Python針對(duì)類實(shí)例進(jìn)行排序相關(guān)操作技巧,需要的朋友可以參考下2018-03-03

