我认为两者是相同的.
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]
但结果却不同.
为什么结果不同?(为什么会产生第二个?)
列表是可变的
列表中的主要部分是列表是可变的.这意味着可以更改列表的值.这就是你遇到麻烦的原因之一.有关详细信息,请参阅文档
评估顺序
另一部分是在解包元组时,评估从左到右开始.有关详细信息,请参阅文档
当你执行a,b = c,d
的值c
和d
首次存储时.然后从左侧开始,首先将值a
更改为c
,然后将值b
更改为d
.
这里的问题是,如果b
在改变值的同时对位置有任何副作用a
,则将d
其分配给后者 b
,这是b
受副作用的影响a
.
现在来解决你的问题
在第一种情况下,
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]
价值保持不变2
.因此print nums
将打印[2, 2, 1]
您可以定义一个类来跟踪该过程:
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
.
这是因为python赋值优先级从左到右.所以在下面的代码中:
nums = [1, 2, 0] nums[nums[0]], nums[0] = nums[0], nums[nums[0]]
它首先分配了nums[0]
to nums[nums[0]]
means nums[1]==1
,然后由于列表是可变对象,nums将是:
[1,1,0]
然后nums[nums[0]]
将分配给nums[0]
哪些方式nums[0]==2
和:
nums = [2,1,0]
就像第二部分一样.
请注意,重要的一点是列表对象是可变的,当您在一段代码中更改它时,它可以就地更改.因此它会影响其余的代码.
评估订单
Python从左到右评估表达式.请注意,在评估分配时,右侧在左侧之前进行评估.