一个关于 += 的谜题

简介: 一个关于 += 的谜题

原文链接:一个关于 += 的谜题


今天在看书过程中发现了一个问题,还挺有意思的,分享给大家。


下面两个 Python 表达式会产生什么结果?


t = (1, 2, [3, 4])
t[2] += [5, 6]
复制代码


给四个备选答案:


  1. t 变成 (1, 2, [3, 4, 5, 6])
  2. 因为 tuple 不支持对它的元素赋值,所以会抛出 TypeError 异常。
  3. 以上两个都不是。
  4. 以上两个都是对的。


当时看到这个问题,第一反应就是选 2。因为 tuple 是不可变对象,不支持对它的元素赋值,会报错。


但事实上,这道题的正解是 4。


在终端里验证一下:


Python 3.8.2 (default, Oct  2 2020, 10:45:42)
[Clang 12.0.0 (clang-1200.0.32.27)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> t = (1, 2, [3, 4])
>>> t[2] += [5, 6]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
复制代码


结果是没问题的,t 被修改了,但是也报错了。


还可以在 Python Tutor 上分析一下:


网站地址:pythontutor.com/


这个网站可以可视化分析 Python 的运行过程和原理。


执行第一个表达式:



df28499e15cd4a4eb0aaf39367d5db36~tplv-k3u1fbpfcp-zoom-in-crop-mark 1304 0 0 0.awebp.png


执行第二个表达式:


d3076e26be8f4b838e68463505397a7b~tplv-k3u1fbpfcp-zoom-in-crop-mark 1304 0 0 0.awebp.png


为什么会这样呢?可以从两个方面来解释:


一、对象类型


Python 中的对象可以分成两类,可变对象和不可变对象,比如一些内置类型:


  1. 可变对象:list,set,dict。
  2. 不可变对象:int,float,bool,string,tuple。


举一个例子:


可变对象:


>>> a = [1, 2, 3]
>>> id(a)
2139167246856
>>> b = a
>>> id(b)
2139167246856
>>> a[1] = 4
>>> a
[1, 4, 3]
>>> b
[1, 4, 3]
>>> id(a)
2139167246856
>>> id(b)
2139167246856


可以看到,改变 a 的同时 b 也跟着变,因为他们始终指向同一个地址。


不可变对象:


>>> a = (1, 2, 3)
>>> id(a)
2139167074776
>>> b = a
>>> a = (4, 5, 6)
>>> a
(4, 5, 6)
>>> b
(1, 2, 3)
>>> id(a)
2139167075928
>>> id(b)
2139167074776


可以看到,a 的值改变后,它的地址也发生了变化,而 b 还是原来的地址,并且原地址中的内容也没有发生变化。


二、字节码


首先解释一下字节码是什么?


Python 执行程序时会把源码文件编译成字节码文件,存放在 __pycahe 目录内,文件用 .pyc 结尾。之后如果不再修改源码文件,运行时则直接使用 .pyc 文件编译成机器码,这样不但运行速度快,而且支持多个操作系统。


字节码,其实就是一种中间代码。


下面用 dis 模块来看一下表达式 s[a] += b 的执行过程:


>>> import dis
>>> dis.dis('s[a] += b')
  1           0 LOAD_NAME                0 (s)
              2 LOAD_NAME                1 (a)
              4 DUP_TOP_TWO
              6 BINARY_SUBSCR
              8 LOAD_NAME                2 (b)
             10 INPLACE_ADD
             12 ROT_THREE
             14 STORE_SUBSCR
             16 LOAD_CONST               0 (None)
             18 RETURN_VALUE
>>>


通过分析字节码,可以看到其中的关键三步:


  1. 4 DUP_TOP_TWO:将 s[a] 存入 TOS(Top Of Stack)。
  2. 10 INPLACE_ADD:执行 TOS += b,带入到文章开头的表达式,就相当于向 t[2] 中添加元素,因为 t[2] 是 list,可变对象,所以这一操作没有问题。
  3. 14 STORE_SUBSCR:将结果保存回 s[a] = TOS,这相当于将结果重新赋值回 t,由于 t 是 tuple,不可变对象,所以报错。


虽然这个问题在平时开发中可能并不常见,但通过分析还是有不少知识点可以深挖的。

简单总结以下三点:


  1. 不要把可变对象放在元组里面。
  2. 增量赋值不是一个原子操作。我们刚才也看到了,它虽然抛出了异常,但还是完成了操作。
  3. 查看 Python 的字节码并不难,而且它对我们了解代码背后的运行机制很有帮助。



目录
相关文章
|
数据采集 机器学习/深度学习 算法
|
安全 数据安全/隐私保护 Android开发
Flutter应用程序加固的问题及解决方案
Flutter应用程序加固的问题及解决方案
267 0
|
安全 网络协议 Unix
网络安全产品之认识安全隔离网闸
随着互联网的发展,网络攻击和病毒传播的方式越来越复杂,对网络安全的要求也越来越高。传统的防火墙设备在面对一些高级的网络攻击时,往往难以做到全面的防护,因此需要一种更加有效的网络安全设备来提高网络的安全性。此外,随着信息技术的不断发展,各个行业对信息系统的依赖程度也越来越高,一旦信息系统遭受攻击或入侵,可能会导致数据泄露、系统瘫痪等严重后果。因此,对于一些高安全级别的网络环境,如政府、军队、公安、银行等,需要一种更加可靠的安全设备来保证网络的安全性。在这样的背景下,安全隔离网闸作为一种新型的网络安全设备应运而生。本文让我们一起来认识安全隔离网闸。
777 0
|
12月前
|
数据采集 存储 NoSQL
提高爬虫性能的 5 个关键技巧:从并发到异步执行
本文介绍了提高网络爬虫性能的五个关键技巧:并发请求、异步执行、使用代理IP、限制请求频率与休眠时间、优化数据提取与存储。结合拼多多的实际案例,展示了如何通过这些技术优化爬虫效率,确保数据采集的高效性和稳定性。
790 0
|
11月前
|
传感器 容器
如何选择适合自己应用场景的水传感器
选择适合应用场景的水传感器需考虑因素包括:水质、测量范围、精度要求、安装环境及成本预算。不同场景如饮用水、工业废水、地下水等需选用不同类型传感器。
418 55
|
数据采集 存储 Web App开发
利用Puppeteer-Har记录与分析网页抓取中的性能数据
在现代网页抓取中,性能数据的记录与分析至关重要。本文介绍如何使用Puppeteer-Har工具记录和分析抓取过程中的性能数据。Puppeteer-Har结合了Puppeteer和Har的优势,简化了性能数据分析流程。首先确保已安装Node.js和npm,并通过`npm install puppeteer puppeteer-har`安装所需库。为了规避IP限制,本文还介绍了如何配置代理IP。最后,通过一个完整示例展示了如何使用Puppeteer-Har抓取今日头条的性能数据,并对生成的HAR文件进行解析和存储。希望本文能为您的网页抓取工作提供帮助。
231 1
利用Puppeteer-Har记录与分析网页抓取中的性能数据
|
存储 缓存 Java
JAVA并发编程系列(11)线程池底层原理架构剖析
本文详细解析了Java线程池的核心参数及其意义,包括核心线程数量(corePoolSize)、最大线程数量(maximumPoolSize)、线程空闲时间(keepAliveTime)、任务存储队列(workQueue)、线程工厂(threadFactory)及拒绝策略(handler)。此外,还介绍了四种常见的线程池:可缓存线程池(newCachedThreadPool)、定时调度线程池(newScheduledThreadPool)、单线程池(newSingleThreadExecutor)及固定长度线程池(newFixedThreadPool)。
|
人工智能 数据管理 Serverless
10分钟获得一位24小时AI专家助手
【8月更文挑战第17天】10分钟获得一位24小时AI专家助手
|
SQL 关系型数据库 分布式数据库
PolarDB产品使用问题之如何关闭或停用自治服务DAS
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
人工智能 UED
提升5.69倍,高效RAG上下文压缩方法COCOM
【8月更文挑战第7天】在AI领域,大型语言模型(LLMs)展现出了强大的计算与知识处理能力,但也面临着处理复杂任务时因上下文信息激增而导致生成时间延长的问题。为解决这一挑战,研究人员开发了COCOM上下文压缩方法,该方法通过将冗长的上下文信息压缩成简洁的上下文嵌入,有效提升了RAG系统的解码速度。实验表明,COCOM能在不牺牲答案质量的前提下,将解码时间最多提升5.69倍,极大改善了用户体验。然而,该方法也可能存在信息损失的风险,且在特定任务上的效果可能受限,因此在实际应用中需综合考量压缩率与答案质量的平衡。论文详情参见:https://arxiv.org/abs/2407.09252。
408 3