當前位置:
首頁 > 知識 > Python 鏈式賦值的坑

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 功能和特點
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球變浪射王 意甲鏈式防守太難破