京公网安备 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-09-04CDA数据分析师 出品 作者:李诗怡 1. 波士顿矩阵(BCG Matrix) 定义: BCG于1970年提出的业务组合分析工具,以"市场增长率"(纵 ...
2026-09-04 数据分析师八成以上的时间在和数据表格打交道,但许多人拿到Excel后习惯性地先算、先分析,结果回头发现漏了一列关键数据, ...
2026-09-04数据透视表是Excel与Power BI中最核心的数据分析工具,具备快速汇总、维度拆分、动态筛选的能力,可高效完成数据归类与统计展示 ...
2026-09-03在Power BI数据分析可视化场景中,堆积柱状图+折线图是最常用的复合图表组合。堆积柱状图适合展示各细分维度当期数值、结构占比 ...
2026-09-03 很多数据分析师每天与Excel打交道,但当被问到“表格结构数据的基本处理单位是什么”“数据类型误判会引发哪些分析错误”“ ...
2026-09-03CDA数据分析师 出品 作者:李诗怡 一、8个核心数据清洗函数 1. TRIM:一键清除多余空格(最常用) 作用:仅保留文本中"单词/字 ...
2026-09-02数据分析的核心并非单纯操作工具、整理报表或绘制图表,而是依靠科学的思维逻辑挖掘数据价值、解释业务现象、指导经营决策。在完 ...
2026-09-02在社会经济、产业研究、区域治理与大数据实证分析中,面板数据是最具研究价值的数据类型。面板数据同时包含截面维度与时间维度信 ...
2026-09-02 很多数据分析师能熟练计算均值、标准差,但当被问到“如何用一张图让业务方3秒内看懂核心结论”“面对不同数据类型该怎么选 ...
2026-09-02在数据驱动决策的体系中,数据分析按照分析目的可分为描述性分析、诊断性分析、预测性分析与指导性分析四大类型。其中,诊断性分 ...
2026-09-01网络请求是Python爬虫开发、接口测试、数据拉取的核心基础功能,Python生态中主要依靠 urllib 和 requests 两大库实现HTTP请求操 ...
2026-09-01 很多数据分析师面对业务问题时,常常感到“知道要分析,却不知道用什么方法”。其实,数据分析并非无章可循——从三大基础范 ...
2026-09-01在数据库设计与业务数据维护中,自增ID是数据表最常用的主键字段,用于唯一标识每一条业务数据,正常状态下ID应保持连续递增。但 ...
2026-08-31在数理统计、数据分析、经济测算与日常量化评估中,平均值是刻画数据集中趋势、反映整体水平的基础核心指标。在实际应用中,最常 ...
2026-08-31在数据驱动的时代,数据分析早已不是“凭经验、靠感觉”的零散操作,而是一套具备固定逻辑、标准化流程的系统方法——这就是数据 ...
2026-08-31在大数据时代背景下,海量行业数据亟需通过专业化工具挖掘潜在价值,辅助企业业务决策、优化运营模式、规避经营风险。Python凭借 ...
2026-08-28SQL是数据分析领域最基础、最核心的工具,承担着取数、清洗、统计、分层、归因的全流程工作。不同于单纯的语法练习,实战化SQL数 ...
2026-08-28 很多企业团队并非缺乏指标,而是陷入“指标失控”:仪表盘上堆满实时跳动的数据,却无法回答“当前瓶颈在哪、下一步该做什么 ...
2026-08-28随着新零售模式的快速普及,零售行业从传统的“货品驱动”全面转向“用户驱动”。门店交易数据、线上消费记录、浏览轨迹、复购频 ...
2026-08-27