当前位置: 首页 > 后端技术 > Python

为什么Python只用一条语句“a,b=b,a”就可以直接交换两个变量?

时间:2023-03-26 13:13:35 Python

Python是一款简单易用的工具。我们在工作中经常会用到它,它也是一门强大的编程语言。广泛应用于数据分析、网页开发、人工智能等行业。但无论是哪个行业、哪个领域,想要熟练使用Python,都必须掌握Python的基础知识。以下文章来自:微信公众号Python猫作者:猫下豌豆花自从接触Python以来,就觉得Python的元组拆包(unpacking)非常有趣,非常简洁易用。最明显的例子就是多重赋值,即在一条语句中同时给多个变量赋值:`x,y=1,2print(x,y)#Result:12`在这个例子中,赋值运算符“=”号右边的两个数会被存入一个元组,变成(1,2),然后解包赋值给“=”号左边的两个变量反过来。如果我们直接写x=1,2然后打印出x,或者在“=”号右边写一个元组,就可以证实这一点:`x=1,2print(x)#Result:(1,2)x,y=(1,2)print(x,y)#Result:12`有些博客或者公众号文章介绍这个特性的时候,一般都会顺带举个例子,就是基于两个变量,直接交换它们的值:`x,y=1,2x,y=y,xprint(x,y)#Result: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解释器在小操作上也提供了几个优化的指令,绝对会超出大多数人的认知