从接触 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 解释器在小小的操作中还提供了几个优化的指令,这绝对会超出大多数人的认知

如果你觉得本文分析得不错,那你应该会喜欢这些文章:

写在最后:本文属于“Python为什么”系列(Python猫出品),该系列主要关注 Python 的语法、设计和发展等话题,以一个个“为什么”式的问题为切入点,试着展现 Python 的迷人魅力。部分话题会推出视频版,请在 B 站收看,观看地址:视频地址

公众号【Python猫】, 本号连载优质的系列文章,有Python为什么系列、喵星哲学猫系列、Python进阶系列、好书推荐系列、技术写作、优质英文推荐与翻译等等,欢迎关注哦。

python两数交换 函数_Python 为什么只需一条语句“a,b=b,a”,就能直接交换两个变量?...相关推荐

  1. setnx和expire合成一条指令_Python 为什么只需一条语句“a,b=b,a”,就能直接交换两个变量?...

    从接触 Python 时起,我就觉得 Python 的元组解包(unpacking)挺有意思,非常简洁好用. 最显而易见的例子就是多重赋值,即在一条语句中同时给多个变量赋值: >>> ...

  2. Python 为什么只需一条语句“a,b=b,a”,就能直接交换两个变量?

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 从接触 Python 时起,我就觉得 Python 的元组解包(u ...

  3. 华为p20pro快捷计算机,华为P20/P20 Pro秒变电脑,只需一条绿联Type C连接线

    原标题:华为P20/P20 Pro秒变电脑,只需一条绿联Type C连接线 就在刚刚,华为发布了其国行P20/P20 PRO新手机,此次P20系列搭载的是麒麟970处理器,采用10nm工艺打造,相比前 ...

  4. 台式计算机设备序列号,只需一条命令,就可以查找计算机序列号,真是方便

    在Windows 10上,在许多情况下需要检查计算机的序列号,例如,当你需要创建连接到家庭网络或组织的设备的清单,或需要联系技术支持来排除问题.订购更换部件或检查保修信息时. 通常可以在笔记本电脑或台 ...

  5. python交换函数_python a,b = b,一个实现?它与C ++交换函数有何不同?

    当我想尝试以下版本的python版本时遇到了这个问题:https : //leetcode.com/problems/first-missing- positive/discuss/17071/My- ...

  6. python中输入数字函数_python中怎么获取输入的数

    python中通过input()函数来获取输入的数.input()函数接收一个标准输入数据,并返回为string类型.函数语法:[input([prompt])],如[age = input(&quo ...

  7. python常见内置函数_python常见的内置函数

    函数分为自定义函数和内置函数 python内置函数分类: 一.强制转换int() / str() / bool() / list() / tuple() / dict() / set() 二.输入输出 ...

  8. python求数的积_python求数组积

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 导语:本文章记录了本人在学习python基础之数据结构篇的重点知识及个人心得,以 ...

  9. python中模块和函数_Python中函数和模块的体验与使用

    函数基础 目标 函数的快速体验 函数的基本使用 函数的参数 函数的返回值 函数的嵌套调用 在模块中定义函数 01. 函数的快速体验 1.1 快速体验 所谓函数,就是把 具有独立功能的代码块 组织为一个 ...

最新文章

  1. 其实,我们需要的并不多
  2. carsim的学习笔记2——test specifications
  3. thinkphp内置标签简单讲解
  4. jieba结巴分词--关键词抽取_结巴中文分词原理分析2
  5. MVVM架构~knockoutjs系列之验证信息自定义输出~续
  6. linux stat 命令查看文件信息
  7. 水刀切割设备行业调研报告 - 市场现状分析与发展前景预测
  8. 套用这套模板,玩转周报、月报、年报更省事
  9. Unbalanced calls to begin/end appearance transitions for
  10. 【重磅】DeepMind开源史上最全强化学习框架OpenSpiel(附安装方法)
  11. React学习笔记—简易信息管理,实现CUD
  12. 关于SEO优化的建议
  13. 复旦961-软件工程笔记
  14. imac下修改本地hosts文件解决react项目中的跨域问题
  15. 手码万字-带你全面了解存储基础知识
  16. verilog语言的ps2键盘驱动设计
  17. asp实训报告摘要_asp实训报告总结.doc
  18. 什么是系统集成资质?对于企业的重要性具体是什么?
  19. 使用python数据分析_我如何使用Python分析《权力游戏》
  20. 关于清理电脑系统垃圾的batch文件

热门文章

  1. Tomcat在server.xml中配置虚拟目录
  2. Shiro + JWT + Spring Boot Restful 简易教程
  3. 不清楚 spring 的这几个知识点,面试直接挂了!
  4. struts2教程--标签库详解
  5. 06 ORA系列:ORA-01741 非法的零长度标识符
  6. 算法--2016搜狐面试:搜狐员工放假了,都玩什么?
  7. 快速幂模板(java)
  8. Python与Java之间的相互调用——Jython
  9. redis的info指令详解
  10. Redis的两种持久化方式