In [55]: a = 5
In [56]: b = 6
In [57]: (a, b) = (b, a)
In [58]: a
Out[58]: 6
In [59]: b
Out[59]: 5
a和b的值的交換如何在內部工作?它絕對不使用臨時變量。如何交換python元組(a,b)=(b,a)中的成員在內部工作?
In [55]: a = 5
In [56]: b = 6
In [57]: (a, b) = (b, a)
In [58]: a
Out[58]: 6
In [59]: b
Out[59]: 5
a和b的值的交換如何在內部工作?它絕對不使用臨時變量。如何交換python元組(a,b)=(b,a)中的成員在內部工作?
Python將右側表達式與左側分配分開。首先對右側進行評估,並將結果存儲在堆棧中,然後使用操作碼分配左側名稱,該操作碼再次從開始取值。
對於具有2個或3項的元組分配,Python的只是使用直接堆棧:
>>> import dis
>>> def foo(a, b):
... a, b = b, a
...
>>> dis.dis(foo)
2 0 LOAD_FAST 1 (b)
3 LOAD_FAST 0 (a)
6 ROT_TWO
7 STORE_FAST 0 (a)
10 STORE_FAST 1 (b)
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
兩個LOAD_FAST
opcodes(其從可變推值壓入堆棧)後,堆棧頂部保持[a, b]
。 ROT_TWO
opcode交換堆棧中的前兩個位置,所以堆棧現在頂部有[b, a]
。這兩個STORE_FAST
opcodes然後將這兩個值存儲在作業左側的名稱中。第一個STORE_FAST
彈出堆棧頂部的值並將其放入a
,下一次彈出,將值存儲在b
中。旋轉是需要的,因爲Python確保左側目標列表中的分配從左到右完成。
對於3個名字的作業,ROT_THREE
後跟ROT_TWO
被執行以反轉堆棧中的前三項。
對於較長的左手側作業,顯式元組被構建:
>>> def bar(a, b, c, d):
... d, c, b, a = a, b, c, d
...
>>> dis.dis(bar)
2 0 LOAD_FAST 0 (a)
3 LOAD_FAST 1 (b)
6 LOAD_FAST 2 (c)
9 LOAD_FAST 3 (d)
12 BUILD_TUPLE 4
15 UNPACK_SEQUENCE 4
18 STORE_FAST 3 (d)
21 STORE_FAST 2 (c)
24 STORE_FAST 1 (b)
27 STORE_FAST 0 (a)
30 LOAD_CONST 0 (None)
33 RETURN_VALUE
在這裏與[d, c, b, a]
堆棧被用來再次從堆棧建立一個元組(以相反的順序,BUILD_TUPLE
啪啪,推結果得到的元組加載到堆棧上),然後UNPACK_SEQUENCE
再次從堆棧中彈出元組,並將所有元素從元組返回到棧上,以便進行STORE_FAST
操作。
後者可能看起來像一個無用的動作,但賦值的右側可能是完全不同的東西,一個函數調用產生元組也許,這樣的Python解釋器做任何假設,並使用UNPACK_SEQUENCE
操作碼永遠。即使對於兩個和三個名稱的分配操作,but a later (peephole) optimization step也可以用上面的ROT_TWO
和ROT_THREE
操作碼取代具有2或3個參數的BUILD_TUPLE
/UNPACK_SEQUENCE
組合以提高效率。
+1 for **因爲Python確保左側目標列表中的分配從左到右完成,所以需要旋轉。**。我不知道。 –
'它絕對不使用臨時變量?'這是一個奇怪的問題。這聽起來像你知道的。 – RedX
您可能感興趣的是用['dis'](http://docs.python.org/3/library/dis.html)查看反彙編代碼。 Spoliers:使用字節碼指令['ROT_TWO'](http://docs.python.org/2/library/dis.html#opcode-ROT_TWO)。 – Kevin
@RedX:如果我知道的話,我不會問這個問題。使用臨時變量是一種簡單的交換方法。 – praveen