一、Algorithm
二、Review
三、Tip
a,b=b,a 剖析
Python 的交换变量操作不完全基于解包操作,有时候是,有时候不是!
-
Python 能在一条语句中实现多重赋值,这是利用了序列解包的特性
-
Python 能在一条语句中实现变量交换,不需引入中间变量,在变量数少于 4 个时(3.8 版本起是少于 5 个),CPython 是利用了 ROT_* 指令来交换栈中的元素,当变量数超出时,则是利用了序列解包的特性。
-
序列解包是 Python 的一大特性,但是在本文的例子中,CPython 解释器在小小的操作中还提供了几个优化的指令,这绝对会超出大多数人的认知
- a,b = 1,2
- 第一步 LOAD_CONST 把“=”号右侧的两个数字作为元组放到栈中
- 第二步 UNPACK_SEQUENCE 是序列解包,接着把解包结果写入局部作用域的变量上。
- a,b = b,a
参照上图
- 两个 LOAD_FAST 是从局部作用域中读取变量的引用,并存入栈中,
- 接着是最关键的 ROT_TWO 操作,它会交换两个变量的引用值,
- 然后两个 STORE_FAST 是将栈中的变量写入局部作用域中。 ROT_TWO 指令是 CPython 解释器实现的对于栈顶两个元素的快捷操作,改变它们指向的引用对象。ROT_THREE 和 ROT_FOUR,分别是快捷交换三和四个变量(摘自:ceval.c 文件,ROT_FOUR是3.8新加入)
-
a,b,c,d = d,c,b,a 同2
-
a,b,c,d,e = e,d,c,b,a BUILD_TUPLE 指令会将给定数量的栈顶元素创建成元组,然后被 UNPACK_SEQUENCE 指令解包,再依次赋值.
值得一提的是,此处之所以比前面的“a,b=1,2”多出一个 build 操作,是因为每个变量的 LOAD_FAST 需要先单独入栈,无法直接被组合成 LOAD_CONST 入栈。也就是说,“=”号右侧有变量时,不会出现前文中的 LOAD_CONST 一个元组的情况。
- a,b = b, 1 赋值语句中有变量, 且 a,b 共两个变量, 则使用ROT_TWO 那几个指令是跟栈中元素的数量有关,而不是跟赋值语句中实际交换的变量数有关.
[https://mp.weixin.qq.com/s/VN8g_wfcWgN5um1G_-Oaw](https://mp.weixin.qq.com/s/VN8g_wfcWgN5um1G-_Oaw)