Python 鏈式賦值的坑
(點擊
上方藍字
,快速關注我們)
編譯:Lin_R
segmentfault.com/a/1190000009325525
如有好文章投稿,請點擊 → 這裡了解詳情
在我們使用Python的過程中, 經常遇到賦值語句, 就像下面的那樣:
a
=
3
b
=
3
可能你會覺得我又要說什麼變數賦值就是引用, 這麼簡單的知識就不討論啦, 相信聰明的大家肯定都知道的, 我想講的是鏈式賦值
先科普下什麼是鏈式賦值:
鏈式賦值: 同時對幾個變數進行賦值
例如:
a = b = c = 3
好了, 現在正式進入正題:
>>>
s
=
[
1
,
2
,
3
,
4
,
5
,
6
]
>>>
i
=
0
>>>
i
=
s
[
i
]
=
3
i 和 s 的值分別是什麼? 可能大家一眼看下去, 就能得出答案:
i
的值
:
3
s
的值
:
[
3
,
2
,
3
,
4
,
5
,
6
]
然而, 這個答案只是對了一半, 因為s的值錯了! 有興趣的朋友可以自行上機試下, 正確答案是:
i
的值
:
3
s
的值
:
[
1
,
2
,
3
,
3
,
5
,
6
]
s的列表, 並沒有像我們想像中的那樣, 就i=0位置上的元素, 變成3, 而是將i=3位置的元素改成3了, 為什麼會這樣? 一起來解析下吧, 上dis大殺器!
[
root
@
iZ23pynfq19Z
~
]
# cat 2.py
s
=
[
1
,
2
,
3
,
4
,
5
]
i
=
0
g
=
i
=
s
[
i
]
=
3
[
root
@
iZ23pynfq19Z
~
]
# python -m dis 2.py
1
0
LOAD
_
CONST
0
(
1
)
3
LOAD
_
CONST
1
(
2
)
6
LOAD
_
CONST
2
(
3
)
9
LOAD
_
CONST
3
(
4
)
12
LOAD
_
CONST
4
(
5
)
15
BUILD
_
LIST
5
18
STORE
_
NAME
0
(
s
)
2
21
LOAD
_
CONST
5
(
0
)
24
STORE
_
NAME
1
(
i
)
3
27
LOAD
_
CONST
2
(
3
)
30
DUP
_
TOP
31
STORE
_
NAME
2
(
g
)
34
DUP
_
TOP
35
STORE
_
NAME
1
(
i
)
38
LOAD
_
NAME
0
(
s
)
41
LOAD
_
NAME
1
(
i
)
44
STORE
_
SUBSCR
45
LOAD
_
CONST
6
(
None
)
48
RETURN_VALUE
第一列的數字, 代表中間的位元組碼是屬於哪一行代碼的.
第1~2行簡單解釋下:
分別LOAD_CONST5個數字, 組成一個列表, 賦值給s,再取一個0, 賦值給i.接下來的就是我們關心的, 也是帶給我們意外的代碼.
第3行:
LOAD_CONST取出常量3, 它並不是像上面執行STORE_NAME, 而是採用DUP_TOP, 這是什麼鬼, 我們這要去看下這指令具體是幹嘛的:
//取自 python/ceval.c
PyEval_EvalFrameEx
(
PyFrameObject *
f
,
int
throwflag
)
{
...
(
省略)
TARGET_NOARG
(
DUP_TOP
)
{
v
=
TOP
();
// 複製運行棧幀的頂部值
Py_INCREF
(
v
);
// 增加引用計數
PUSH
(
v
);
// 再壓入運行棧幀
FAST_DISPATCH
();
}
...
(
省略)
}
DUP_TOP指令說白了, 就是將剛才LOAD_CONST指令取出的常量3, 複製一份給v,然後再壓回去運行棧幀, 這樣就有兩個3了, 為什麼要這麼做, 可能個大家已經猜到了, 不過我們還是得繼續看具體是不是像我們想的那樣, 繼續看會位元組碼:
35
STORE
_
NAME
1
(
i
)
38
LOAD
_
NAME
0
(
s
)
41
LOAD
_
NAME
1
(
i
)
44
STORE_SUBSCR
果然不出我們所料, 開始將這些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, 所以在它們的鏈式賦值中, 是將右邊表達式的返回值, 再賦值給左邊的, 例如:
a
=
s
=
3
等價於
:
a
=
(
s
=
3
)
也就是
s
=
3
返回
3
,
再賦值給
a
而在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
看完本文有收穫?請轉
發分享給更多人
關注「Python開發者」,提升Python技能


※你需要知道的、有用的 Python 功能和特點
※Python 的 Sequence 切片下標問題
※用 Python 寫一個簡單的Web框架
※如何用Python對數據進行差分
TAG:Python |
※Python鏈式操作:PyFunctional
※Itamar Willner:電極上的雜交鏈式反應用於放大的電化學分析和平行分析
※夏日新爆款?Nike將推出「Fanny pack」拉鏈式收納拖鞋
※HubblePhone概念智能手機:鉸鏈式多屏+5G網路+雙CPU+雙系統
※提高區塊鏈並發性,TrustNote想用有向無環圖突破鏈式結構的瓶頸
※HelloEOS梓岑:嘻哈外表下的區塊鏈式達爾文主義
※「大毒蛇III」鏈式炮進入試驗場 50mm口徑威力大幅提升
※蘋果申請了鉸鏈式OLED屏專利,未來實體鍵盤或將被淘汰
※蘋果鉸鏈式OLED屏專利:帶增強可視性和抑制反射的雙屏裝
※水的第四種狀態:在地下5000米處形成,以1600km/h的速度鏈式擴增
※波音777X噴氣客機的鉸鏈式摺疊翼尖設計通過FAA批准
※鏈式供彈系統
※戰壕屠殺者,美國大毒蛇30MM鏈式機關炮,步兵無處可藏
※車載武器選用的是25毫米鏈式炮,美國M2步兵戰車曾是一代經典
※馬蜂窩:日本「櫻花季」超級IP如何拉動旅遊產業鏈式發展
※創意的手鏈式充電線
※一個多元化的多鏈式協議
※M242型鏈式機關炮,陸地最強單兵機槍,戰鬥機都能打下來
※聚合酶鏈式反應
※C羅哭了!3場23射0球變浪射王 意甲鏈式防守太難破