元组解包顺序更改分配的值
Tuple unpacking order changes values assigned
我觉得两者是一样的
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 个要点
列表是可变的
列表的主要部分是列表是可变的。这意味着
列表的值可以更改。这是你的原因之一
面对麻烦。 Refer the docs for more info
评估顺序
另一部分是在解包元组时,评估开始
从左到右。 Refer the docs for more info
简介
当您执行 a,b = c,d
时,首先存储 c
和 d
的值。然后从左边开始,先把a
的值改成c
,再把b
的值改成d
。
这里要注意的是,如果在更改 a
的值时对 b
的位置有任何副作用,那么 d
将分配给 later 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]
因为在下面的代码中python赋值优先级留给了right.So:
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 evaluates expressions from left to right. Notice that while evaluating an assignment, the right-hand side is evaluated before the left-hand side.
在第一个示例中,nums[1] 被设置为 1,然后 nums[0] 被设置为 2,如您所料。
在第二个例子中,nums[0] 被设置为 2,然后 nums[2] 被设置为 1。这是因为,在这种情况下,左边手边的 nums[nums[0]] 在赋值发生时实际上引用了 nums[2],因为 nums[0] 刚刚被设置为 2.
你可以定义一个class来跟踪进程:
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
.
我觉得两者是一样的
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 个要点
列表是可变的
列表的主要部分是列表是可变的。这意味着 列表的值可以更改。这是你的原因之一 面对麻烦。 Refer the docs for more info
评估顺序
另一部分是在解包元组时,评估开始 从左到右。 Refer the docs for more info
简介
当您执行 a,b = c,d
时,首先存储 c
和 d
的值。然后从左边开始,先把a
的值改成c
,再把b
的值改成d
。
这里要注意的是,如果在更改 a
的值时对 b
的位置有任何副作用,那么 d
将分配给 later 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]
因为在下面的代码中python赋值优先级留给了right.So:
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 evaluates expressions from left to right. Notice that while evaluating an assignment, the right-hand side is evaluated before the left-hand side.
在第一个示例中,nums[1] 被设置为 1,然后 nums[0] 被设置为 2,如您所料。
在第二个例子中,nums[0] 被设置为 2,然后 nums[2] 被设置为 1。这是因为,在这种情况下,左边手边的 nums[nums[0]] 在赋值发生时实际上引用了 nums[2],因为 nums[0] 刚刚被设置为 2.
你可以定义一个class来跟踪进程:
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
.