Python 鏈式賦值的坑

發表於2017-05-26

在我們使用Python的過程中, 經常遇到賦值語句, 就像下面的那樣:

可能你會覺得我又要說什麼變數賦值就是引用, 這麼簡單的知識就不討論啦, 相信聰明的大家肯定都知道的, 我想講的是鏈式賦值

先科普下什麼是鏈式賦值:

例如:

好了, 現在正式進入正題:

i 和 s 的值分別是什麼? 可能大家一眼看下去, 就能得出答案:

然而, 這個答案只是對了一半, 因為s的值錯了! 有興趣的朋友可以自行上機試下, 正確答案是:

s的列表, 並沒有像我們想象中的那樣, 就i=0位置上的元素, 變成3, 而是將i=3位置的元素改成3了, 為什麼會這樣? 一起來解析下吧, 上dis大殺器!

第一列的數字, 代表中間的位元組碼是屬於哪一行程式碼的.

第1~2行簡單解釋下:
分別LOAD_CONST5個數字, 組成一個列表, 賦值給s,再取一個0, 賦值給i.接下來的就是我們關心的, 也是帶給我們意外的程式碼.

第3行:
LOAD_CONST取出常量3, 它並不是像上面執行STORE_NAME, 而是採用DUP_TOP, 這是什麼鬼, 我們這要去看下這指令具體是幹嘛的:

DUP_TOP指令說白了, 就是將剛才LOAD_CONST指令取出的常量3, 複製一份給v,然後再壓回去執行棧幀, 這樣就有兩個3了, 為什麼要這麼做, 肯呢個大家已經猜到了, 不過我們還是得繼續看具體是不是像我們想的那樣, 繼續看會位元組碼:

果然不出我們所料, 開始將這些3通過STORE_NAME賦值給i, 而對於s, 它反而是, 再一次LOAD_NAME取出i的值, 此時i的值是3, 不是一開始的0了, 在通過STORE_SUBSCR指令, 將剛才壓入執行時時棧的3賦值給位置是3的元素, 具體的原始碼就不再看, 到這就夠了.
所以看到這, 相信大家都能清楚, 為什麼結果是 [1, 2, 3, 3, 5, 6]

這跟我們想象中的鏈式賦值很不同, 我們以前總是覺得, 賦值要從右到左依次執行, 先執行 s[i] = 3, 再執行 i=3, 然而這些是類似c語言這類支援表示式賦值才允許的. 在c語言中, s = 3表示式是有返回值的. 它會返回賦值的結果3, 所以在它們的鏈式賦值中, 是將右邊表示式的返回值, 再賦值給左邊的, 例如:

而在python是不支援這種表示式賦值的, 也就是表示式是沒有返回值的, 如果硬要a = (s = 3)只會觸發SyntaxError: invalid syntax
希望大家以後在用到這種鏈式賦值時, 儘量避免這些問題哦


感謝@Daetalus 童鞋指出問題:

支援表示式賦值是Python語言的核心,比如a = b + 3。這裡的 b + 3
就是表示式。如果不支援表示式賦值就沒法繼續下去了。

a = (s = 3)出錯的原因是因為s = 3是賦值語句,而不是表示式。

Python的表示式是由操作符連線而成的,但“=”在Python中並不是操作符(Operator),只是語法分隔符(Delimiters)。參見:https://docs.python.org/release/3.6.1/reference/lexical_analysis.html#operators

相關文章