setnx和expire合成一条指令_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.ROT_FOUR
Lifts second, third and forth stack items one position up, moves top down to position four.
New in version 3.8.
CPython 应该是以为这几种变量的交换操作很常见,因此才提供了专门的优化指令。就像 [-5,256] 这些小整数被预先放到了整数池里一样。
对于更多变量的交换操作,实际上则会用到前面说的解包操作:
截图中的 BUILD_TUPLE 指令会将给定数量的栈顶元素创建成元组,然后被 UNPACK_SEQUENCE 指令解包,再依次赋值。
值得一提的是,此处之所以比前面的“a,b=1,2”多出一个 build 操作,是因为每个变量的 LOAD_FAST 需要先单独入栈,无法直接被组合成 LOAD_CONST 入栈。也就是说,“=”号右侧有变量时,不会出现前文中的 LOAD_CONST 一个元组的情况。
最后还有一个值得一提的细节,那几个指令是跟栈中元素的数量有关,而不是跟赋值语句中实际交换的变量数有关。看一个例子就明白了:
分析至此,你应该明白前文中的结论是怎么回事了吧?
我们稍微总结一下:
- Python 能在一条语句中实现多重赋值,这是利用了序列解包的特性
- Python 能在一条语句中实现变量交换,不需引入中间变量,在变量数少于 4 个时(3.8 版本起是少于 5 个),CPython 是利用了 ROT_* 指令来交换栈中的元素,当变量数超出时,则是利用了序列解包的特性。
- 序列解包是 Python 的一大特性,但是在本文的例子中,CPython 解释器在小小的操作中还提供了几个优化的指令,这绝对会超出大多数人的认知
如果你觉得本文分析得不错,那你应该会喜欢这些文章:
1、Python为什么使用缩进来划分代码块?
2、Python 的缩进是不是反人类的设计?
3、Python 为什么不用分号作语句终止符?
4、Python 为什么没有 main 函数?为什么我不推荐写 main 函数?
5、Python 为什么推荐蛇形命名法?
6、Python 为什么不支持 i++ 自增语法,不提供 ++ 操作符?
写在最后:本文属于“Python为什么”系列(Python猫出品),该系列主要关注 Python 的语法、设计和发展等话题,以一个个“为什么”式的问题为切入点,试着展现 Python 的迷人魅力。部分话题会推出视频版,请在 B 站收看,观看地址:视频地址
http://weixin.qq.com/r/PCksNLzErOHTrfhX93wu (二维码自动识别)
公众号【Python猫】, 本号连载优质的系列文章,有Python为什么系列、喵星哲学猫系列、Python进阶系列、好书推荐系列、技术写作、优质英文推荐与翻译等等,欢迎关注哦。
setnx和expire合成一条指令_Python 为什么只需一条语句“a,b=b,a”,就能直接交换两个变量?...相关推荐
- python两数交换 函数_Python 为什么只需一条语句“a,b=b,a”,就能直接交换两个变量?...
从接触 Python 时起,我就觉得 Python 的元组解包(unpacking)挺有意思,非常简洁好用. 最显而易见的例子就是多重赋值,即在一条语句中同时给多个变量赋值: >>> ...
- Python 为什么只需一条语句“a,b=b,a”,就能直接交换两个变量?
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 从接触 Python 时起,我就觉得 Python 的元组解包(u ...
- 华为p20pro快捷计算机,华为P20/P20 Pro秒变电脑,只需一条绿联Type C连接线
原标题:华为P20/P20 Pro秒变电脑,只需一条绿联Type C连接线 就在刚刚,华为发布了其国行P20/P20 PRO新手机,此次P20系列搭载的是麒麟970处理器,采用10nm工艺打造,相比前 ...
- 台式计算机设备序列号,只需一条命令,就可以查找计算机序列号,真是方便
在Windows 10上,在许多情况下需要检查计算机的序列号,例如,当你需要创建连接到家庭网络或组织的设备的清单,或需要联系技术支持来排除问题.订购更换部件或检查保修信息时. 通常可以在笔记本电脑或台 ...
- python安装进度条不动_python – tkinter中的进度条不起作用
我正在写一个小应用程序来复制一些文件.我做了几乎所有我想要的东西,但有三件事: 1)复制选项处于运动状态时移动的进度条.我可以显示它,但它不会反应. 我用这个来表明它: self.p = ttk.Pr ...
- java ganymed ssh2_java ganymed-ssh2,session 执行多条指令,最好可以在前一条的基础上,执行下一条。...
通过session.requestDumbPTY()和session.startShell()来模拟命令行,在通过io流操作 try { session = conn.openSession(); s ...
- 只需一条信息即可远程利用严重的思科 Jabber RCE缺陷
聚焦源代码安全,网罗国内外最新资讯! 作者:Lindsey O'Donnell 编译:奇安信代码卫士团队 研究人员警告称,思科 Jabber 的 Windows 版中存在一个严重的远程代码执行 (R ...
- windows命令_用Windows恢复环境(WinRE)排除电脑故障,只需一条命令
在Windows10上,Windows Recovery Environment(Windows恢复环境,WinRE)是一项功能,它将自动修复.重置为出厂默认设置.系统映像恢复和其他故障排除工具捆绑在 ...
- Ubuntu 20.04 LTS安装搜狗输入法,只需三条命令,还能自动更新
link 第一步换ubuntu 的源 Ubuntu20.04软件源更换 1 按照官方教程添加ukui的官方源[1],其中包含了麒麟版的搜狗输入法.同时我们也能够及时和上游保持更新. curl -sL ...
最新文章
- C enum(枚举)
- Java-Runoob:Java Stream、File、IO
- java中继承、多接口实现
- Spark _17 _SparkShuffle、HashShuffleManager、SortShuffleManager
- beautifulsoup获取属性_Python爬虫常用模块:BeautifulSoup
- ajax ashx session 为空,jquery ajax post 方式ashx文件取不到值。
- 数理统计与数据分析第三版习题 第3章 第33-35题
- 在这里,北上深成为“社科之城”
- 深入浅出DockerPDF
- Mysql探索之索引详解,又能和面试官互扯了~,java分布式系统面试题
- 决策理论与方法——决策概念与分类
- 基于ssm医药药品管理系统
- java 节日_java节假日
- MACD金叉启动副图/选股指标 通达信 源码 有未来
- 经典算法——筛选法求素数(素数筛选)
- gateway内置过滤器工厂
- 未来的计算机阅读短文,未来的计算机阅读答案
- Hadoop-Impala优化十大指导原则和最佳实践
- linux 利用cat写文件名,在LINUX中如何用cat创建一个文件
- linux查看db2数据库字符集,如何创建一个使用gbk字符集的mydb2数据库?
热门文章
- 分库分表的几种常见形式以及可能遇到的难
- php截取字符串,带中文,多余的省略号代替
- Mysql-索引的基础和类型
- springboot单例模式注入对象_Spring 中经典的 9 种设计模式,打死也要记住啊!
- Java 比较两个版本号的大小 (通用)
- Android之判断时间戳是不是今天
- linux shell之$?和得到联合使用命令的结果
- Eclipse运行项目报Could not find *.apk!解决办法
- 我使用 html 反向输出自己打自己(7)
- 我让代码生了个孩子继承了他爸爸谁知他爸爸继承了他爷爷(16)