自从接触了Python,就觉得Python的元组拆包(unpacking)非常有趣,非常简洁易用。最明显的例子就是多重赋值,即在一条语句中同时给多个变量赋值:>>>x,y=1,2>>>print(x,y)#Result:12本例中,赋值操作符“=”右边的两个数会被存入一个元组,变成(1,2),然后解包,赋值给运算符“=”左边的两个数“=”依次签到。多变的。如果我们直接写x=1,2然后打印x,或者在“=”号右边写一个元组,就可以证实这一点:>>>x=1,2>>>print(x)#结果:(1,2)>>>x,y=(1,2)>>>print(x,y)#Result:12一些博客或者公众号文章介绍这个功能的时候,他们通常会举个例子,基于两个变量,直接交换它们的值:>>>x,y=1,2>>>x,y=y,x>>>print(x,y)#结果:21一般来说,交换两个变量的操作需要引入第三个变量。道理很简单,想要调换两个杯子里的水,自然需要第三个容器作为中转。但是Python的写法不需要使用中间变量,其形式和前面的解包赋值是一样的。因为这种类似的形式,很多人误认为Python的变量交换操作也是基于拆包操作。但真的是这样吗?我四处搜索,发现有人试图回答这个问题,但他们的回答大多不够全面。(当然有很多错误的答案,更多的人只是知道而已,却从来没有想过。)先放出本文的答案:Python的交换变量操作并不完全基于拆包操作,有时是,有时不是!你觉得这个答案很神奇吗?这是闻所未闻的吗?!到底发生了什么?我们先来看看标题中最简单的两个变量。下面我们去dis大杀器看看编译后的字节码:上图打开了两个窗口,可以很方便的对比“a,b=b,a”和“a,b=1,2”的区别:a,b=b,a”操作:两个LOAD_FAST是引用,从局部作用域读取变量并入栈,接下来是最关键的ROT_TWO操作,它会交换两个变量的引用值,然后是两个STORE_FAST就是将栈中的变量写入局部作用域。“a,b=1,2”操作:第一步LOAD_CONST将“=”号右边的两个数作为元组入栈,第二步UNPACK_SEQUENCE为序列拆包,然后写入拆包结果进入本地范围内的变量。显然,这两种形式相似的写法实际上执行的是不同的操作。在交换变量的操作中,没有打包和解包的步骤!ROT_TWO指令是CPython解释器对栈顶的两个元素实现的快捷操作,改变它们指向的引用对象。类似的还有两条指令ROT_THREE和ROT_FOUR,分别是交换三变量和四变量的快捷方式(摘自:ceval.c文件,最新3.9分支):预定义的栈顶操作如下:查看官方文档,这些解释两条指令,其中ROT_FOUR是3.8版本新添加的:ROT_TWOSwapsthetwotop-moststackitems.ROT_THREELliftssecondandthirdstackitemsup,movestopdowntopositionthree.ROT_FOURLsLiftssecond,thirdandfourthstackitemsuponepositionup,自上而下移动到位置四。3.8版中的新功能。CPython应该是觉得这几类变量的交换操作很常见,所以提供了专门的优化说明。就像[-5,256]这些小整数被预先放置在整数池中。对于更多变量的交换操作,实际上会用到上面提到的拆包操作:截图中的BUILD_TUPLE指令会将给定数量的栈顶元素创建成元组,然后被UNPACK_SEQUENCE指令拆包,依次赋值.值得一提的是,之所以比之前的“a,b=1,2”多了一个build操作,是因为每个变量的LOAD_FAST需要先单独入栈,不能直接组合成LOAD_CONST并推入堆栈。也就是说,当“=”号右边有变量的时候,就不会出现上一篇LOAD_CONST一个元组的情况了。最后,还有一个细节值得一提。这些指令与堆栈中元素的数量有关,与赋值语句中实际交换的变量数量无关。看个例子就明白了:分析到这里,你应该明白上一篇文章的结论是怎么回事了吧?总结一下:Python可以在一条语句中实现多次赋值,利用了序列拆包的特性。Python可以在一条语句中交换变量而无需引入中间变量。当变量个数小于4时(3.8CPython使用ROT_*指令交换栈中的元素,当变量个数超过时,使用序列拆包的特性。序列拆包是Python的一大特性,但是在本文的例子,CPython解释器在小操作上也提供了几个优化的指令,肯定会超出大多数人的认知。那么,那你应该会喜欢这些文章:1.为什么Python要用缩进来划分代码块?2.是Python的缩进是反人类的设计?3.为什么Python不使用分号作为语句结束符?4.为什么Python没有main函数?为什么我不推荐写main函数?5.为什么Python推荐蛇形命名法?6.为什么Python不支持i++自增语法,也不提供++运算符?写在最后:本文属于《WhyPython》系列(Python猫出品),主要集中在Python的语法、设计和开发等主题。它以每一个“为什么”的问题为切入点,试图展示Python的魅力。魅力。部分专题会有视频版,请到B站观看,观看地址:视频地址公众号【Python猫】,本号连载优质系列文章,包括WhyPython系列、喵星哲学猫系列、Python进阶系列、好书推荐系列、技术写作、优质英文推荐与翻译等,欢迎关注。
