2015-07-13 477 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吗? –

相关问题