2014-01-10 43 views
18
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)中的成員在內部工作?

+0

'它絕對不使用臨時變量?'這是一個奇怪的問題。這聽起來像你知道的。 – RedX

+1

您可能感興趣的是用['dis'](http://docs.python.org/3/library/dis.html)查看反彙編代碼。 Spoliers:使用字節碼指令['ROT_TWO'](http://docs.python.org/2/library/dis.html#opcode-ROT_TWO)。 – Kevin

+0

@RedX:如果我知道的話,我不會問這個問題。使用臨時變量是一種簡單的交換方法。 – praveen

回答

54

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_TWOROT_THREE操作碼取代具有2或3個參數的BUILD_TUPLE/UNPACK_SEQUENCE組合以提高效率。

+1

+1 for **因爲Python確保左側目標列表中的分配從左到右完成,所以需要旋轉。**。我不知道。 –

相關問題