一个关于 += 的谜题
原文链接: 一个关于 += 的谜题
今天在看书过程中发现了一个问题,还挺有意思的,分享给大家。
下面两个 Python 表达式会产生什么结果?
给四个备选答案:
t
变成(1, 2, [3, 4, 5, 6])
。因为 tuple 不支持对它的元素赋值,所以会抛出
TypeError
异常。以上两个都不是。
以上两个都是对的。
当时看到这个问题,第一反应就是选 2。因为 tuple 是不可变对象,不支持对它的元素赋值,会报错。
但事实上,这道题的正解是 4。
在终端里验证一下:
结果是没问题的,t
被修改了,但是也报错了。
还可以在 Python Tutor 上分析一下:
网站地址: https://pythontutor.com/
这个网站可以可视化分析 Python 的运行过程和原理。
执行第一个表达式:
执行第二个表达式:
为什么会这样呢?可以从两个方面来解释:
一、对象类型
Python 中的对象可以分成两类,可变对象和不可变对象,比如一些内置类型:
可变对象:list,set,dict。
不可变对象:int,float,bool,string,tuple。
举一个例子:
可变对象:
可以看到,改变 a
的同时 b
也跟着变,因为他们始终指向同一个地址。
不可变对象:
可以看到,a
的值改变后,它的地址也发生了变化,而 b
还是原来的地址,并且原地址中的内容也没有发生变化。
二、字节码
首先解释一下字节码是什么?
Python 执行程序时会把源码文件编译成字节码文件,存放在 __pycahe 目录内,文件用 .pyc
结尾。之后如果不再修改源码文件,运行时则直接使用 .pyc
文件编译成机器码,这样不但运行速度快,而且支持多个操作系统。
字节码,其实就是一种中间代码。
下面用 dis 模块来看一下表达式 s[a] += b
的执行过程:
通过分析字节码,可以看到其中的关键三步:
4 DUP_TOP_TWO
:将s[a]
存入 TOS(Top Of Stack)。10 INPLACE_ADD
:执行TOS += b
,带入到文章开头的表达式,就相当于向t[2]
中添加元素,因为t[2]
是 list,可变对象,所以这一操作没有问题。14 STORE_SUBSCR
:将结果保存回s[a] = TOS
,这相当于将结果重新赋值回t
,由于t
是 tuple,不可变对象,所以报错。
虽然这个问题在平时开发中可能并不常见,但通过分析还是有不少知识点可以深挖的。
简单总结以下三点:
不要把可变对象放在元组里面。
增量赋值不是一个原子操作。我们刚才也看到了,它虽然抛出了异常,但还是完成了操作。
查看 Python 的字节码并不难,而且它对我们了解代码背后的运行机制很有帮助。
以上就是本文的全部内容,如果觉得还不错的话,欢迎点赞和转发,多谢
推荐阅读:
版权声明: 本文为 InfoQ 作者【AlwaysBeta】的原创文章。
原文链接:【http://xie.infoq.cn/article/4f76f01917d4d657e2efb6045】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论