元组解包顺序更改分配的值

时间:2015-12-09 05:26:54

标签: python list indexing tuple-packing

我认为两者完全相同。

nums = [1, 2, 0]    
nums[nums[0]], nums[0] = nums[0], nums[nums[0]]    
print nums  # [2, 1, 0]

nums = [1, 2, 0]    
nums[0], nums[nums[0]] = nums[nums[0]], nums[0]    
print nums  # [2, 2, 1] 

但结果不同。
为什么结果不同? (为什么会产生第二个?)

4 个答案:

答案 0 :(得分:47)

先决条件 - 2个要点

  • 列表是可变的

    列表中的主要部分是列表是可变的。这意味着 列表的值可以更改。这就是你的原因之一 面对麻烦。 Refer the docs for more info

  • 评估顺序

    另一部分是在解包元组时,评估开始 从左到右。 Refer the docs for more info

简介

当您执行a,b = c,d时,首先会存储cd的值。然后从左侧开始,a的值首先更改为c,然后b的值更改为d

这里的问题是,如果在更改b的值时a的位置有任何副作用,则d稍后会分配给 bba的副作用影响。

用例

现在出现问题

在第一种情况下,

nums = [1, 2, 0]    
nums[nums[0]], nums[0] = nums[0], nums[nums[0]]    

nums[0]最初为1nums[nums[0]]2,因为它的评估结果为nums[1]。因此1,2现在存储在内存中。

现在,元组拆包从左侧发生,所以

nums[nums[0]] = nums[1] = 1   # NO side Effect. 
nums[0] = 2

因此print nums将打印[2, 1, 0]

然而在这种情况下

nums = [1, 2, 0]   
nums[0], nums[nums[0]] = nums[nums[0]], nums[0]    

nums[nums[0]], nums[0]将2,1放在堆栈上,就像第一种情况一样。

然而,在左侧,即nums[0], nums[nums[0]]nums[0]的更改会产生副作用,因为它被用作nums[nums[0]]中的索引。因此

nums[0] = 2
nums[nums[0]] = nums[2] = 1  # NOTE THAT nums[0] HAS CHANGED

nums[1]在值2保持不变。因此print nums将打印[2, 2, 1]

答案 1 :(得分:18)

您可以定义一个类来跟踪该过程:

class MyList(list):
    def __getitem__(self, key):
        print('get ' + str(key))
        return super(MyList, self).__getitem__(key)
    def __setitem__(self, key, value):
        print('set ' + str(key) + ', ' + str(value))
        return super(MyList, self).__setitem__(key, value)

对于第一种方法:

nums = MyList([1, 2, 0])
nums[nums[0]], nums[0] = nums[0], nums[nums[0]]

输出是:

get 0
get 0
get 1
get 0
set 1, 1
set 0, 2

第二种方法:

nums = MyList([1, 2, 0])
nums[0], nums[nums[0]] = nums[nums[0]], nums[0]

输出是:

get 0
get 1
get 0
set 0, 2
get 0
set 2, 1

在这两种方法中,前三行与元组生成相关,而后三行与赋值相关。第一种方法的右侧元组是(1, 2),第二种方法是(2, 1)

在分配阶段,第一个方法获取nums[0] 1,然后设置nums[1] = 1,然后设置nums[0] = 2,第二个方法分配nums[0] = 2,然后获取nums[0] 2,最后设置为nums[2] = 1

答案 2 :(得分:10)

因为python分配优先级从左到右。所以在下面的代码中:

 nums = [1, 2, 0]
 nums[nums[0]], nums[0] = nums[0], nums[nums[0]]

首先将nums[0]分配给nums[nums[0]]表示nums[1]==1然后由于列表是可变对象,因此nums将是:

[1,1,0]

然后nums[nums[0]]将被分配到nums[0],这意味着nums[0]==2并且:

nums = [2,1,0]

第二部分也是如此。

请注意,重要的一点是列表对象是可变的,当您在一段代码中更改它时,它可以就地更改。因此它会影响其余的代码。

Evaluation order

  

Python从左到右评估表达式。请注意,在评估分配时,右侧会在左侧之前进行评估。

答案 3 :(得分:3)

在第一个例子中,nums [1]被设置为1,然后nums [0]被设置为2,正如您所料。

在第二个示例中,nums [0]设置为2,然后 nums [2] 设置为1.这是因为,在这种情况下,左侧是nums [nums]当赋值发生时,[0]实际上是引用nums [2],因为nums [0]刚被设置为2。