从接触 Python 时起,我就以为 Python 的元组解包(unpacking)挺有意思,很是简洁好用。python
最显而易见的例子就是多重赋值,即在一条语句中同时给多个变量赋值:ide
>>> x, y = 1, 2 >>> print(x, y) # 结果:1 2
在此例中,赋值操做符“=”号的右侧的两个数字会被存入到一个元组中,即变成 (1,2),而后再被解包,依次赋值给“=”号左侧的两个变量。函数
若是咱们直接写x = 1,2
,而后打印出 x,或者在“=”号右侧写成一个元组,就能证明到这一点:优化
>>> x = 1, 2 >>> print(x) # 结果:(1, 2) >>> x, y = (1, 2) >>> print(x, y) # 结果:1 2
一些博客或公众号文章在介绍到这个特性时,一般会顺着举一个例子,即基于两个变量,直接交换它们的值:ui
>>> x, y = 1, 2 >>> x, y = y, x >>> print(x, y) # 结果:2 1
通常而言,交换两个变量的操做须要引入第三个变量。道理很简单,若是要交换两个杯子中所装的水,天然会须要第三个容器做为中转。spa
然而,Python 的写法并不须要借助中间变量,它的形式就跟前面的解包赋值同样。正由于这个形式类似,不少人就误觉得 Python 的变量交换操做也是基于解包操做。翻译
可是,事实是否如此呢?设计
我搜索了一番,发现有人试图回答过这个问题,可是他们的回答基本不够全面。(固然,有很多是错误的答案,还有更多人只是知其然,却从未想过要知其因此然)code
先把本文的答案放出来吧:Python 的交换变量操做不彻底基于解包操做,有时候是,有时候不是!视频
有没有以为这个答案很神奇呢?是否是闻所未闻?!
到底怎么回事呢?先来看看标题中最简单的两个变量的状况,咱们上dis
大杀器看看编译的字节码:
上图开了两个窗口,能够方便比较“a,b=b,a”与“a,b=1,2”的不一样:
很明显,形式类似的两种写法实际上完成的操做并不相同。在交换变量的操做中,并无装包和解包的步骤!
ROT_TWO 指令是 CPython 解释器实现的对于栈顶两个元素的快捷操做,改变它们指向的引用对象。
还有两个相似的指令是 ROT_THREE 和 ROT_FOUR,分别是快捷交换三和四个变量(摘自:ceval.c 文件,最新的 3.9 分支):
预约义的栈顶操做以下:
查看官方文档中对于这几个指令的解释,其中 ROT_FOUR 是 3.8 版本新加的:
ROT_TWO
Swaps the two top-most stack items.
ROT_THREE
Lifts second and third stack item one position up, moves top down to position three.
ROT_FOUR
Lifts second, third and forth stack items one position up, moves top down to position four.
New in version 3.8.
CPython 应该是觉得这几种变量的交换操做很常见,所以才提供了专门的优化指令。就像 [-5,256] 这些小整数被预先放到了整数池里同样。
对于更多变量的交换操做,实际上则会用到前面说的解包操做:
截图中的 BUILD_TUPLE 指令会将给定数量的栈顶元素建立成元组,而后被 UNPACK_SEQUENCE 指令解包,再依次赋值。
值得一提的是,此处之因此比前面的“a,b=1,2”多出一个 build 操做,是由于每一个变量的 LOAD_FAST 须要先单独入栈,没法直接被组合成 LOAD_CONST 入栈。也就是说,“=”号右侧有变量时,不会出现前文中的 LOAD_CONST 一个元组的状况。
最后还有一个值得一提的细节,那几个指令是跟栈中元素的数量有关,而不是跟赋值语句中实际交换的变量数有关。看一个例子就明白了:
分析至此,你应该明白前文中的结论是怎么回事了吧?
咱们稍微总结一下:
若是你以为本文分析得不错,那你应该会喜欢这些文章:
四、Python 为何没有 main 函数?为何我不推荐写 main 函数?
六、Python 为何不支持 i++ 自增语法,不提供 ++ 操做符?
写在最后:本文属于“Python为何”系列(Python猫出品),该系列主要关注 Python 的语法、设计和发展等话题,以一个个“为何”式的问题为切入点,试着展示 Python 的迷人魅力。部分话题会推出视频版,请在 B 站收看,观看地址:视频地址
公众号【Python猫】, 本号连载优质的系列文章,有Python为何系列、喵星哲学猫系列、Python进阶系列、好书推荐系列、技术写做、优质英文推荐与翻译等等,欢迎关注哦。