2015-07-13 476 views
17

代碼:x,y = y,x在Python中交換值的邏輯是什麼?

x="salil" 
y="Ajay" 
y,x=x,y 

print x +" "+ y 

輸出:

Ajay salil 

,這是什麼邏輯的解釋嗎?

+9

邏輯?這就是它的工作原理。第一個右邊被分配到第一個左邊,第二個右邊被分配到第二個左邊,同時。什麼不清楚呢? – Amadan

+2

我想知道編譯器如何讀取它,更優先於和=運算符在這裏的任何問題? –

+0

相關:http://stackoverflow.com/questions/14836228/is-there-a-standardized-method-to-swap-two-variables-in-python – NightShadeQueen

回答

52

這一機制的運作方式是兩個特徵的組合 - 形成隱元組和元組/列表解包。

當你做something = x, y時,Python會做的是隱式地創建一個tuple(一種不可變列表),它由兩個元素x和y組成。所以,下面的代碼兩行是完全等價的:

something = x, y 
something = (x, y) 

的元組,當然可以,包含兩個以上要素:

something = a, b, c, d, e 
something = (a, b, c, d, e) 

此功能的打算用例是使其更容易/清潔劑做的事情一樣返回多個值從一個函數:

def foo(): 
    return "hello", "world" 

第二反饋eature是元組/列表開箱。每當你在左邊有一系列變量,以及任何類型的列表,元組或其他集合時,Python都會嘗試將右邊的每個元素與左邊的元素進行匹配:

>>> a, b, c = [11, 22, 33] 
>>> print(a) 
11 
>>> print(b) 
22 
>>> print(c) 
33 

如果有幫助,行a, b, c = [11, 22, 33]基本上等同於做:

temp = [11, 22, 33] 
a = temp[0] 
b = temp[1] 
c = temp[2] 

注意,右側可以從字面上任何類型的集合,而不只是元組或列表。所以下面的代碼是有效的:

>>> p, q = "az" 
>>> print(p + " " + q) 
a z 
>>> 
>>> s, t = {'cat': 'foo', 'dog': 'bar'} 
>>> print(s + " " + t) 
cat dog 

(雖然,因爲在Python字典沒有義務要在任何特定的順序,而且由於鍵的順序可以自由炒,拆包他們可能不會因爲每次你都可能得到不同的結果。)

如果變量的數量和集合中的元素數量不匹配,Python會拋出異常:

>>> a, b = (1, 2, 3) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
ValueError: too many values to unpack (expected 2) 

>>> a, b, c = (1, 2) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
ValueError: need more than 2 values to unpack 

因此,這意味着,如果我們從上面傳來叫我們foo功能,做以下內容:

>>> x, y = foo() 

...變量x就等於字符串"hello"和可變y就等於字符串"world"


所以,最後,這意味着你原來的代碼片段:

x = "salil" 
y = "Ajay" 
y, x = x, y 

...在邏輯上等同於以下內容:

x = "salil" 
y = "Ajay" 
temp = (x, y) # evaluates to ("salil", "Ajay") 
y, x = temp 

...甚至細分更多,在邏輯上等同於以下內容:

x = "salil" 
y = "Ajay" 
temp = (x, y) # evaluates to ("salil", "Ajay") 
y = temp[0] 
x = temp[1] 

請注意,您可以將這兩種操作分開進行。首先形成並評估元組,然後將元組解壓縮回變量。最終結果是您的兩個變量的值互換。 (但是,事實證明,CPython解釋器(Python的原始和'標準'實現)在這裏做了一些優化:它將優化交換並且不會完成整個元組的解包 - 參見下面的評論我不確定是否Python的其他實現也是這樣做的,儘管我懷疑它們可能會這樣做。無論如何,這種優化是針對特定實現的,並且獨立於Python語言本身的設計。)

+0

解釋器是否首先進行打包或分配? –

+2

@salilvishnuKapur在任務發生前,總是對右手邊進行全面評估。對於Python和大多數編程語言而言,情況總是如此),而不僅僅是用元組打包/解包。 – Michael0x2a

+0

但解釋器沒有從左到右讀取每行代碼? –

5

這適用於交換,因爲首先評估=的右側。

所以在右邊,它的計算結果

'salil', 'Ajay' 

,然後的xy分配情況

x, y = 'salil', 'Ajay' 
+0

但解釋器不會從左到右讀取每行代碼? –

+7

@salilvishnuKapur它*掃描*和*解析*的方式,但它不*執行*的方式。否則,運營商的優先權和事實上的分配將不可能。 – EJP

+0

@EJP好的,謝謝!=運算符右側的 –

10

好吧,讓我們來看看:

import dis 
src = ''' 
x="salil" 
y="Ajay" 
y,x=x,y 

print x +" "+ y 
''' 
code = compile(src, '<string>', 'exec') 
dis.dis(code) 

這將產生:

2   0 LOAD_CONST    0 ('salil') 
       3 STORE_NAME    0 (x) 

    3   6 LOAD_CONST    1 ('Ajay') 
       9 STORE_NAME    1 (y) 

    4   12 LOAD_NAME    0 (x) 
      15 LOAD_NAME    1 (y) 
      18 ROT_TWO    
      19 STORE_NAME    1 (y) 
      22 STORE_NAME    0 (x) 

    6   25 LOAD_NAME    0 (x) 
      28 LOAD_CONST    2 (' ') 
      31 BINARY_ADD   
      32 LOAD_NAME    1 (y) 
      35 BINARY_ADD   
      36 PRINT_ITEM   
      37 PRINT_NEWLINE  
      38 LOAD_CONST    3 (None) 
      41 RETURN_VALUE   

請記住,Python作爲堆棧機器運行。在這種情況下,它優化了對ROT_TWO(即交換)指令的分配。還有一個ROT_THREE指令。但是,讓我們嘗試別的東西:

import dis 
src = 'x, y, z, w = a, b, c, d' 
code = compile(src, '<string>', 'exec') 
dis.dis(code) 

由此產生的一般形式:

1   0 LOAD_NAME    0 (a) 
       3 LOAD_NAME    1 (b) 
       6 LOAD_NAME    2 (c) 
       9 LOAD_NAME    3 (d) 
      12 BUILD_TUPLE    4 
      15 UNPACK_SEQUENCE   4 
      18 STORE_NAME    4 (x) 
      21 STORE_NAME    5 (y) 
      24 STORE_NAME    6 (z) 
      27 STORE_NAME    7 (w) 
      30 LOAD_CONST    0 (None) 
      33 RETURN_VALUE   
+0

你能解釋一下這個更詳細的LOAD_FAST,LOAD_FAST,ROT_TWO嗎? –

相關問題