本文主要通过实例来解释 Python 一些赋值的实现细节。

批量赋值

先算等号右边的值(从左到右算),然后依次赋给等号左边的变量(从左到右赋)

常量批量赋值

1
2
3
4
5
6
7
8
dis.dis('a, b, c = 10, 20, 30')
#     0 LOAD_CONST               0 ((10, 20, 30))
#     2 UNPACK_SEQUENCE          3
#     4 STORE_NAME               0 (a)
#     6 STORE_NAME               1 (b)
#     8 STORE_NAME               2 (c)
#    10 LOAD_CONST               1 (None)
#    12 RETURN_VALUE

步骤解释:

  1. 0 LOAD_CONST 0:将第 0 个常量推入栈顶,此时栈内只有 1 个元素 (10, 20, 30)。
  2. 2 UNPACK_SEQUENCE 2:将栈顶元素解包为 3 个元素,并按照从右到左的顺序推入栈顶,此时栈内从顶到底的元素依次为 10、20、30。
  3. 4 STORE_NAME 0:将栈顶元素赋给第 0 个变量(即 a = 10)。
  4. 6 STORE_NAME 1:将栈顶元素赋给第 1 个变量(即 b = 20)。
  5. 8 STORE_NAME 2:将栈顶元素赋给第 2 个变量(即 c = 30)。

变量批量赋值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 批量赋值
a, b, c = 10, 20, 30

# 反汇编
dis.dis('a, b, c = c, b, a')

#     0 LOAD_NAME                0 (c)
#     2 LOAD_NAME                1 (b)
#     4 LOAD_NAME                2 (a)
#     6 ROT_THREE
#     8 ROT_TWO
#    10 STORE_NAME               2 (a)
#    12 STORE_NAME               1 (b)
#    14 STORE_NAME               0 (c)
#    16 LOAD_CONST               0 (None)
#    18 RETURN_VALUE

步骤解释:

  1. 0 LOAD_NAME 0:将第 0 个变量推入栈顶,此时栈内元素 c;
  2. 2 LOAD_NAME 1:将第 1 个变量推入栈顶,此时栈内元素 b、c;
  3. 4 LOAD_NAME 2:将第 2 个变量推入栈顶,此时栈内元素 a、b、c;
  4. 6 ROT_THREE:将栈顶第 2、3 项向上提升一个位置,原栈顶动到第 3 位,此时栈内元素 b、c、a。
  5. 8 ROT_TWO:交换栈顶前 2 项,此时栈内元素 c、b、a。
  6. ...

超过三个变量赋值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
dis.dis('a, b, c, d = d, c, b, a')
#     0 LOAD_NAME                0 (d)
#     2 LOAD_NAME                1 (c)
#     4 LOAD_NAME                2 (b)
#     6 LOAD_NAME                3 (a)
#     8 BUILD_TUPLE              4
#    10 UNPACK_SEQUENCE          4
#    12 STORE_NAME               3 (a)
#    14 STORE_NAME               2 (b)
#    16 STORE_NAME               1 (c)
#    18 STORE_NAME               0 (d)
#    20 LOAD_CONST               0 (None)
#    22 RETURN_VALUE

前面的指令比较熟悉了,这里就不赘述。关键在于 8 BUILD_TUPLE 与 10 UNPACK_SEQUENCE 这两句指令,前者将栈内元素取出逆序组成元组(栈内元素 a、b、c、d 取出之后形成元组 (a, b, c, d)),后者按照从右到左的顺序又把该元组的元素压入栈(栈内元素变成 d、c、b、a),这两句结合在一起其实相当于一个「逆序」操作。

在 Python 中,超过 3 个变量的批量赋值都会使用到这种方式赋值。

例子:赋值顺序会影响结果

1
2
3
4
5
6
k = 0
m = [1, 2]
k, m[k] = m[k], 6

print(k)  # 1
print(m)  # [1, 6]

连续赋值

先算最右边的值,然后依次赋给左边的变量(从左往右赋)

1
2
3
4
5
6
a = [10, 20]
b = a
a[0] = a = [6]

print(a)  # [6]
print(b)  # [[6], 20]

参考

▲