在Python的语法中,可以在一行中不通过临时变量,即可交换变量。如:a, b = b, a
。
在直觉上,a, b = b, a
与b, a = a, b
得到的a, b值应当相等。但在实际中,如果a, b 在赋值过程中,存在引用关系的情况下,则得到值就会有差异,以下是一次字节码的详细分析。
假设有一个数组为
nums = [1, 2, 3, 4]
,此时执行nums[0], nums[1] = nums[1], nums[0]
后,不难得出nums变为[2, 1, 3, 4]
但如果执行
nums[nums[1]], nums[1] = nums[1], nums[nums[1]]
和nums[1], nums[nums[1]] = nums[nums[1]], nums[1]
, 此时nums的值将不太能确定。以下使用Python3.8对
nums[0], nums[1] = nums[1], nums[0]
对执行过程进行分析import dis def test(): nums = [1, 2, 3, 4] nums[0], nums[1] = nums[1], nums[0] dis.dis(test) # 得到nums[0], nums[1] = nums[1], nums[0]行的执行过程为 24 LOAD_FAST 0 (nums) 26 LOAD_CONST 1 (1) 28 BINARY_SUBSCR 30 LOAD_FAST 0 (nums) 32 LOAD_CONST 5 (0) 34 BINARY_SUBSCR 36 ROT_TWO 38 LOAD_FAST 0 (nums) 40 LOAD_CONST 5 (0) 42 STORE_SUBSCR 44 LOAD_FAST 0 (nums) 46 LOAD_CONST 1 (1) 48 STORE_SUBSCR 50 LOAD_CONST 0 (None) 52 RETURN_VALUE # co_consts (None, 1, 2, 3, 4, 0) # co_varnames ('nums',)
字节码说明
code DESC LOAD_FAST 将指向局部对象 co_varnames[var_num]
的引用推入栈顶。LOAD_CONST 将 co_consts[consti]
推入栈顶。BINARY_SUBSCR 先POP得到k值,再对栈顶执行 d = d[k] ROT_TWO 交换两个最顶层的堆栈项。 STORE_SUBSCR 实现 TOS1[TOS] = TOS2
。再将栈顶头三个移出RETURN_VALUE 返回 TOS 到函数的调用者。 得出流程图为
可以看出,在等号两边执行时,都为从左到右执行。
因此分析以下赋值结果
nums = [1, 2, 3, 4]
nums[nums[1]], nums[1] = nums[1], nums[nums[1]]
nums[nums[1]], nums[1] = 2, 3
nums[2], nums[1] = 2, 3
nums = [1, 3, 2, 4]nums = [1, 2, 3, 4]
nums[1], nums[nums[1]] = nums[nums[1]], nums[1]
nums[1], nums[nums[1]] = 3, 2
nums[1] = 3
nums[3] = 2
nums = [1, 3, 3, 2]
```