Tuple解包顺序更改分配的值
我认为两者是相同的。
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]
但结果是不同的。
为什么结果不同? (为什么是第二个结果?)
先决条件 – 2个重要的要点
-
列表是可变的
列表中的主要部分是列表是可变的。 这意味着列表的值可以改变。 这是你面临麻烦的原因之一。 请参阅文档以获取更多信息
-
评估顺序
另一部分是在解包一个元组时,评估从左到右。 请参阅文档以获取更多信息
介绍
当你做a,b = c,d
,首先存储c
和d
的值。 然后从左边开始, a
的值首先变为c
,然后b
的值变为d
。
这里的问题是,如果在改变b
的值的同时对b
的位置有任何副作用,则d
被分配给后面的 b
,这是受b
的副作用影响的b
。
用例
现在来解决你的问题
在第一种情况下,
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]
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[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]
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]
分配给nums[nums[0]]
意味着nums[1]==1
,然后由于列表是可变对象,所以nums将是:
[1,1,0]
然后将nums[nums[0]]
赋值给nums[0]
,这意味着nums[0]==2
并且:
nums = [2,1,0]
第二部分就是这样
请注意,这里重要的一点是列表对象是可变的,当你在一段代码中改变它时,它可以在原地改变。 因此会影响到其他代码。
评估顺序
Python从左向右评估expression式。 请注意,在评估分配时,右侧将在左侧进行评估。
在第一个例子中,nums [1]被设置为1,nums [0]被设置为2,正如你所期望的那样。
在第二个例子中,nums [0]被设置为2,然后nums [2]被设置为1.这是因为在这种情况下,左侧的nums [nums [0]]实际上是引用了nums [2 ],因为nums [0]刚被设置为2。