50

我认为两者是相同的。

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

4 回答 4

51

先决条件- 2 个要点


介绍

当您执行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]最初是1并且nums[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]在 value 处保持不变2。因此print nums将打印[2, 2, 1]

于 2015-12-09T05:39:37.023 回答
21

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

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)

在赋值阶段,第一个方法 get nums[0]which is1和 set nums[1] = 1,然后nums[0] = 2,第二个方法 assign nums[0] = 2,然后 get nums[0]which is 2,最后 set nums[2] = 1

于 2015-12-09T05:58:06.797 回答
11

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

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

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

[1,1,0]

然后nums[nums[0]]将被分配到nums[0]哪个意思nums[0]==2和:

nums = [2,1,0]

第二部分也是如此。

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

评估顺序

Python 从左到右计算表达式。请注意,在评估分配时,右侧先于左侧评估。

于 2015-12-09T05:43:21.380 回答
4

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

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

于 2015-12-09T05:45:32.507 回答