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 ,首先存储cd的值。 然后从左边开始, 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]最初为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] 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。

Interesting Posts