京公网安备 11010802034615号
经营许可证编号:京B2-20210330
作者:大奎
来源:早起Python
Python 程序员肯定知道 a,b = b,a,这句话用来交换两个变量。相较于其它语言需要引入一个 temp 来临时存储变量的做法,Python 的这种写法无疑非常优雅。
佶屈聱牙的 C 写法:
int a = 1; int b = 2; int temp; temp = a; a = b; b = temp;
简洁优雅的 Python 写法:
a,b = 1,2 a,b = b,a
虽然语法非常方便,但我们始终不曾想过:它是怎么运作的?背后支撑它的机制是什么?下面让我们一步步分析它。
最常见的解释是:
a,b = b,a 中右侧是元组表达式,即 b,a 是一个两个元素的 tuple(a,b)。表达式左侧是两个待分配元素,而 = 相当于元组元素拆包赋值操作。
这种方法,理解起来最简单,但实际是这种情况么?
让我们从字节码上看下,是不是这种情况。
大家可能不太了解 Python 字节码。Python 解释器是一个基于栈的虚拟机。Python 解释器就是编译、解释 Python 代码的二进制程序。
虚拟机是一种执行代码的容器,相较于二进制代码具有方便移植的特点。而 Python 的虚拟机就是栈机器。
Python 中函数调用、变量赋值等操作,最后都转换为对栈的操作。这些对栈的具体操作,就保存在字节码里。
dis 模块可以反编译字节码,使其变成人类可读的栈机器指令。如下,我们看反编译 a,b=b,a 的代码。
>>> import dis >>> dis.dis("a,b=b,a") 1 0 LOAD_NAME 0 (b) 2 LOAD_NAME 1 (a) 4 ROT_TWO 6 STORE_NAME 1 (a) 8 STORE_NAME 0 (b) 10 LOAD_CONST 0 (None) 12 RETURN_VALUE
可见,在 Python 虚拟机的栈上,我们按照表达式右侧的 b,a 的顺序,先后压入计算栈中,然后用一个重要指令 ROT_TWO,这个操作交换了 a 和 b 的位置,最后 STORE_NAME 操作将栈顶的两个元素先后弹出,传递给 a 和 b 元素。
栈的特性是先进后出(FILO)。当我们按b,a顺序压入栈的时候,弹出时先出的就是a,再弹出就是b。STORE_NAME指令会把栈顶元素弹出,并关联到相应变量上。
如果没有第 4 列的指令 ROT_TWO,此次 STORE_NAME 弹出的第一个变量会是后压栈的 a,这样就是 a=a 的效果。有了 ROT_TWO 则完成了变量的交换。
好了,我们知道靠压栈、弹栈和交换栈顶的两个元素,实现了 a,b = b,a 的操作。
同时,我们也知道了,上诉元组拆包赋值的说法,是不恰当的。
那 ROT_TWO 是怎么具体操作的呢?
见名知意,可以猜出来 ROT_TWO 是交换两个栈顶变量的操作。在 Python 源代码的层面上,来看是如何交换两个栈顶的元素。
下载 Python 源代码,进入 Python/ceval.c 文件,在 1101 行,我们看到了 ROT_TWO 的操作。
TARGET(ROT_TWO){ PyObject *top = TOP(); PyObject *second = SECOND(); SET_TOP(second); SET_SECOND(top); FAST_DISPATCH(); }
代码比较简单,我们用 TOP 和 SECOND 宏获取了栈上的 a,b 元素,然后再用 SET_TOP、SET_SECOND 宏把值写入栈中。以此完成交换栈顶元素的操作。
下面,我们来看一个奇怪的现象,在这篇文章里,也可以看到这个现象。如下,我们试图排序这个列表:
>>> a = [0, 1, 3, 2, 4] >>> a[a[2]], a[2] = a[2], a[a[2]] >>> a >>> [0, 1, 2, 3, 4] >>> a = [0, 1, 3, 2, 4] >>> a[2], a[a[2]] = a[a[2]],a[2] >>> a >>> [0, 1, 3, 3, 4]
按照理解 a,b = b,a 和 b,a=a,b 是一样的结果,但从上例中我们看到,这两者的结果是不同的。
导致这一现象的原因在于:求值的顺序。毫无疑问,整个表达式先求右侧的两个元素,然后作为常数保存起来。最后赋值给左侧的两个变量。
最后赋值时,需要注意,我们从左到右依次赋值,如果 a[2] 先修改的话,势必会影响到其后的 a[a[2]] 的列表下标。
“
你可以使用反汇编代码,来分析产生这个现象的具体步骤。
”
当我们使用常数作为右侧元组,来给左侧变量赋值时;或使用超过三个元素,来完成便捷交换时,其在字节码层次上便不是 ROT_TWO 这种操作了。
>>> dis.dis("a,b,c,d=b,c,d,a") 1 0 LOAD_NAME 3 LOAD_NAME 6 LOAD_NAME 9 LOAD_NAME 12 BUILD_TUPLE 15 UNPACK_SEQUENCE 18 STORE_NAME 21 STORE_NAME 24 STORE_NAME 27 STORE_NAME 30 LOAD_CONST 33 RETURN_VALUE >>>
很明显,这里是在偏移 12 字节处 BUILD_TUPLE 组装元组,然后解包赋值给左侧变量。上文所述的通俗说法,在这里又成立了!
也就是说,当小于四个元素交换时,Python 采用优化的栈操作来完成交换。
当使用常量或者超过四个元素时,采用元组拆包赋值的方式来交换。
至于为什么是四个元素,应该是因为 Python 最多支持到 ROT_THREE 操作,四个元素的话,系统不知道该怎么优化了。但在新版本的 Python 中,我看到了 ROT_FOUR 操作,所以这时候,四个元素还是 ROT_* 操作来优化的。
>>>import opcode >>>opcode.opmap["ROT_THREE"] 3
此例中,该版本 Python 支持 ROT_THREE 操作,你也可以使用 ROT_FOUR 查看自己 Python 是否支持,进而确定是否可以四个以上元素便捷交换。
综上,我们了解了 Python 中优雅的 a,b = b,a 交换变量方法的实现和运行原理,深入了解其求值顺序和局限性,同时学习了深入分析 Python 代码的思路和方法,如果对你有帮助,可以给本文点个赞,也欢迎分享出去让更多人看见!
数据分析咨询请扫描二维码
若不方便扫码,搜微信号:CDAshujufenxi
在企业经营决策中,销售额预测是核心环节之一——无论是库存备货、营销预算制定、产能规划,还是战略布局,都需要基于精准的销售 ...
2026-03-09金融数据分析的核心价值,是通过挖掘数据规律、识别风险、捕捉机会,为投资决策、风险控制、业务优化提供精准支撑——而这一切的 ...
2026-03-09在数据驱动决策的时代,CDA(Certified Data Analyst)数据分析师的核心工作,是通过数据解读业务、支撑决策,而指标与指标体系 ...
2026-03-09在数据处理的全流程中,数据呈现与数据分析是两个紧密关联却截然不同的核心环节。无论是科研数据整理、企业业务复盘,还是日常数 ...
2026-03-06在数据分析、数据预处理场景中,dat文件是一种常见的二进制或文本格式数据文件,广泛应用于科研数据、工程数据、传感器数据等领 ...
2026-03-06在数据驱动决策的时代,CDA(Certified Data Analyst)数据分析师的核心价值,早已超越单纯的数据清洗与统计分析,而是通过数据 ...
2026-03-06在教学管理、培训数据统计、课程体系搭建等场景中,经常需要对课时数据进行排序并实现累加计算——比如,按课程章节排序,累加各 ...
2026-03-05在数据分析场景中,环比是衡量数据短期波动的核心指标——它通过对比“当前周期与上一个相邻周期”的数据,直观反映指标的月度、 ...
2026-03-05数据治理是数字化时代企业实现数据价值最大化的核心前提,而CDA(Certified Data Analyst)数据分析师作为数据全生命周期的核心 ...
2026-03-05在实验检测、质量控制、科研验证等场景中,“方法验证”是确保检测/分析结果可靠、可复用的核心环节——无论是新开发的检测方法 ...
2026-03-04在数据分析、科研实验、办公统计等场景中,我们常常需要对比两组数据的整体差异——比如两种营销策略的销售额差异、两种实验方案 ...
2026-03-04在数字化转型进入深水区的今天,企业对数据的依赖程度日益加深,而数据治理体系则是企业实现数据规范化、高质量化、价值化的核心 ...
2026-03-04在深度学习,尤其是卷积神经网络(CNN)的实操中,转置卷积(Transposed Convolution)是一个高频应用的操作——它核心用于实现 ...
2026-03-03在日常办公、数据分析、金融理财、科研统计等场景中,我们经常需要计算“平均值”来概括一组数据的整体水平——比如计算月度平均 ...
2026-03-03在数字化转型的浪潮中,数据已成为企业最核心的战略资产,而数据治理则是激活这份资产价值的前提——没有规范、高质量的数据治理 ...
2026-03-03在Excel办公中,数据透视表是汇总、分析繁杂数据的核心工具,我们常常通过它快速得到销售额汇总、人员统计、业绩分析等关键结果 ...
2026-03-02在日常办公和数据分析中,我们常常需要探究两个或多个数据之间的关联关系——比如销售额与广告投入是否正相关、员工出勤率与绩效 ...
2026-03-02在数字化运营中,时间序列数据是CDA(Certified Data Analyst)数据分析师最常接触的数据类型之一——每日的营收、每小时的用户 ...
2026-03-02在日常办公中,数据透视表是Excel、WPS等表格工具中最常用的数据分析利器——它能快速汇总繁杂数据、挖掘数据关联、生成直观报表 ...
2026-02-28有限元法(Finite Element Method, FEM)作为工程数值模拟的核心工具,已广泛应用于机械制造、航空航天、土木工程、生物医学等多 ...
2026-02-28