语句Python 为什么只需一条语句“a,b=b,a”,就能直接交换两个变量?
本文插图
从接触 Python 时起 , 我就觉得 Python 的元组解包(unpacking)挺有意思 , 非常简洁好用 。
最显而易见的例子就是多重赋值 , 即在一条语句中同时给多个变量赋值:
>>> 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
一些博客或公众号文章在介绍到这个特性时 , 通常会顺着举一个例子 , 即基于两个变量 , 直接交换它们的值:
>>> x, y = 1, 2>>> x, y = y, x>>> print(x, y) # 结果:2 1
一般而言 , 交换两个变量的操作需要引入第三个变量 。 道理很简单 , 如果要交换两个杯子中所装的水 , 自然会需要第三个容器作为中转 。
然而 , 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_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.
推荐阅读
- 真理科技原创 知道为什么自己的Vlog不如别人的好吗?飞宇VLOG pocket2体验
- 海峡网|李健姚勇为什么退出水木年华,水木年华成员资料
- 娱乐大起底|我的世界:为什么绿宝石矿最稀有,绿宝石却不是?
- 韩剧集合处|博主的衣服你也有,为什么穿出来不够高级?这些细节值得注意
- 茶余玩历史|喊话中方:为什么要取消我们的国籍,大量美籍华人被“驱逐出境”
- 君笺雅侃红楼|贾琮为什么没有参加,他的结局又如何?,贾母中秋夜宴
- 吻戏|吻戏为什么只拍上半身,当大家看到下半身的道具,这是喜剧吗?
- 魔女斗篷|王者荣耀:为什么说只要有魔女斗篷存在,法师时代就不可能到来
- 涉案14亿公安局长竟是黑老大|涉案14亿公安局长竟是黑老大 为什么如此猖狂?
- 宇航员|为什么中国第一女宇航员刘洋,回地球后不露脸,她怎么了吗?
