Python 多变量赋值的机制
在刷LeeCode反转链表这道题时(输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL),遇到了一个很有意思的问题。
以下是解答区给出的实现方法,采用多变量同时赋值
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
cur, pre = head, None
while cur:
cur.next, pre, cur = pre, cur, cur.next ### ---1
return pre
但是当稍微改变一下—1这一行赋值语句的顺序,如修改为 pre, cur, cur.next = cur, cur.next, pre 则会报错。为了搞清楚这其中的曲折,我们先来了解一下python中多变量赋值的机制。
Python自带了一个字节码工具叫dis,通过dis我们可以看到多变量赋值的真相。
import dis
def foo():
t=1
t,a=3,t+1
dis.dis(foo)
输出的如下
21 0 LOAD_CONST 1 (1)
2 STORE_FAST 0 (t)
22 4 LOAD_CONST 2 (3)
6 LOAD_FAST 0 (t)
8 LOAD_CONST 1 (1)
10 BINARY_ADD
12 ROT_TWO
14 STORE_FAST 0 (t)
16 STORE_FAST 1 (a)
18 LOAD_CONST 0 (None)
20 RETURN_VALUE
Python解释器是基于栈的虚拟机,LOAD_CONST和LOAD_FAST分别是把常量和变量入栈的操作,ROT_TWO是把栈顶两个元素翻转。赋值的时候虚拟机栈里面数据变化如下
# 第一步入栈
vm_stack = [..., 3, t+1] = [..., 3, 2]
# 第二步翻转
vm_stack = [..., 2, 3]
# 第三步赋值
t = vm_stack.pop() = 3
a = vm_stack.pop() = 2
简单来说,可以等价为先计算等式右边各项的值并且保存为一个元组。随后将这个中间变量–元组保存一下,再以从左到右的顺序一一对应赋值给左边变量:
t = 1
t,a=3,t+1
3 , t+1 ===> (3,2)
temp = (3,2)
t,a = temp
回看原来的代码:
在修改后的代码中,由于元组中的值从左到右依次赋给pre cur cur.next, 在将③赋值给cur后,cur指向了data为4的结点,因此cur.next代表的不再是data为3的结点的指针域,因此产生了错误。