详解Python中表达式i += x与i = i + x是否等价


Posted in Python onFebruary 08, 2017

前言

最近看到一个题目,看似很简单,其实里面有很深的意义,题目是Python 表达式 i += x 与 i = i + x 等价吗?如果你的回答是yes,那么恭喜你正确了50%,为什么说只对了一半呢? 按照我们的一般理解它们俩是等价的,整数操作时两者没什么异同,但是对于列表操作,是不是也一样呢?

先看下面两段代码:

代码1

>>> l1 = range(3)
>>> l2 = l1
>>> l2 += [3]
>>> l1
[0, 1, 2, 3]
>>> l2
[0, 1, 2, 3]

代码2

>>> l1 = range(3)
>>> l2 = l1
>>> l2 = l2 + [3]
>>> l1
[0, 1, 2]
>>> l2
[0, 1, 2, 3]

代码1与代码2中的l2的值是一样的,但是l1的值却不一样,说明 i += x 与 i = i + x 是不等价的,那什么情况下等价,什么情况下不等价呢?

弄清楚这个问题之前,首选得明白两个概念:可变对象与不可变对象。

在 Python 中任何对象都有的三个通用属性:唯一标识、类型、值。

唯一标识:用于标识对象的在内存中唯一性,它在对象创建之后就不会再改变,函数 id()可以查看对象的唯一标识

类型:决定了该对象支持哪些操作,不同类型的对象支持的操作就不一样,比如列表可以有length属性,而整数没有。同样地对象的类型一旦确定了就不会再变,函数 type()可以返回对象的类型信息。

对象的值与唯一标识不一样,并不是所有的对象的值都是一成不变的,有些对象的值可以通过某些操作发生改变,值可以变化的对象称之为可变对象(mutable),值不能改变的对象称之为不可变对象(immutable)

不可变对象(immutable)

对于不可变对象,值永远是刚开始创建时候的值,对该对象做的任何操作都会导致一个新的对象的创建。

>>> a = 1
>>> id(a)
32574568
>>> a += 1
>>> id(a)
32574544

整数 “1” 是一个不可变对象,最初赋值的时候,a 指向的是整数对象 1 ,但对变量a执行 += 操作后, a 指向另外一个整数对象 2 ,但对象 1 还是在那里没有发生任何变化,而 变量 a 已经指向了一个新的对象2。常见的不可变对象有:int、tuple、set、str。

详解Python中表达式i += x与i = i + x是否等价

可变对象(mutable)

可变对象的值可以通过某些操作动态的改变,比如列表对象,可以通过append方法不断地往列表中添加元素,该列表的值就在不断的处于变化中,一个可变对象赋值给两个变量时,他们共享同一个实例对象,指向相同的内存地址,对其中任何一个变量操作时,同时也会影响另外一个变量。

>>> x = range(3)
>>> y = x

>>> id(x)
139726103041232
>>> id(y)
139726103041232

>>> x.append(3)
>>> x
[0, 1, 2, 3]
>>> y
[0, 1, 2, 3]

>>> id(x)
139726103041232
>>> id(y)
139726103041232

详解Python中表达式i += x与i = i + x是否等价

执行append操作后,对象的内存地址不会改变,x、y 依然指向的是原来同一个对象,只不过是他的值发生了变化而已。

详解Python中表达式i += x与i = i + x是否等价

理解完可变对象与不可变对象后,回到问题本身,+= 与 +的区别在哪里呢?

+= 操作首先会尝试调用对象的 __iadd__方法,如果没有该方法,那么尝试调用__add__方法,先来看看这两个方法有什么区别

__add__和 __iadd__ 的区别

  1. __add__ 方法接收两个参数,返回它们的和,两个参数的值均不会改变。
  2. __iadd__ 方法同样接收两个参数,但它是属于 in-place 操作,就是说它会改变第一个参数的值,因为这需要对象是可变的,所以对于不可变对象没有__iadd__方法。
>>> hasattr(int, '__iadd__')
False
>>> hasattr(list, '__iadd__')
True

显然,整数对象是没有__iadd__的,而列表对象提供了__iadd__方法。

>>> l2 += [3] # 代码1:使用__iadd__,l2的值原地修改

代码1中的 += 操作调用的是__iadd__方法,他会原地修改l2指向的那个对象本身的值

详解Python中表达式i += x与i = i + x是否等价

>>> l2 = l2 + [3] # 代码2:调用 __add__,创建了一个新的列表,赋值给了l2

而代码2中的 + 操作调用的是 __add__ 方法,该方法会返回一个新的对象,原来的对象保持不变,l1还是指向原来的对象,而l2已经指向一个新的对象。

详解Python中表达式i += x与i = i + x是否等价

以上就是表达式 i += x 与 i = i + x 的区别。因此对于列表进行 += 操作时,会存在潜在的bug,因为l1会因为l2的变化而发生改变,就像函数的参数不宜使用可变对象作为关键字参数一样。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Python 相关文章推荐
python 简易计算器程序,代码就几行
Aug 29 Python
为python设置socket代理的方法
Jan 14 Python
Python随手笔记之标准类型内建函数
Dec 02 Python
Python3.5面向对象程序设计之类的继承和多态详解
Apr 24 Python
PyTorch搭建多项式回归模型(三)
May 22 Python
Windows下实现将Pascal VOC转化为TFRecords
Feb 17 Python
浅谈PyTorch的可重复性问题(如何使实验结果可复现)
Feb 20 Python
Jupyter Notebook 实现正常显示中文和负号
Apr 24 Python
详解Python3.8+PyQt5+pyqt5-tools+Pycharm配置详细教程
Nov 02 Python
Django中日期时间型字段进行年月日时分秒分组统计
Nov 27 Python
Python编程编写完善的命令行工具
Sep 15 Python
Python 数据可视化神器Pyecharts绘制图像练习
Feb 28 Python
Python实现的多线程http压力测试代码
Feb 08 #Python
Python实现定时任务
Feb 08 #Python
python使用matplotlib绘制柱状图教程
Feb 08 #Python
Python实现简单的多任务mysql转xml的方法
Feb 08 #Python
python使用matplotlib绘制折线图教程
Feb 08 #Python
关于Python中Inf与Nan的判断问题详解
Feb 08 #Python
python3.5实现socket通讯示例(TCP)
Feb 07 #Python
You might like
随时给自己贴的图片加文字的php水印
2007/03/16 PHP
在laravel框架中实现封装公共方法全局调用
2019/10/14 PHP
Laravel中如何轻松容易的输出完整的SQL语句
2020/07/26 PHP
Aster vs KG BO3 第二场2.18
2021/03/10 DOTA
js onload处理html页面加载之后的事件
2013/10/30 Javascript
Js操作树节点自动折叠展开的几种方法
2014/05/05 Javascript
jquery操作HTML5 的data-*的用法实例分享
2014/08/17 Javascript
详解addEventListener的三个参数之useCapture
2015/03/16 Javascript
简介AngularJS中使用factory和service的方法
2015/06/17 Javascript
nodejs和php实现图片访问实时处理
2017/01/05 NodeJs
解决vue-cli单页面手机应用input点击手机端虚拟键盘弹出盖住input问题
2018/08/25 Javascript
Vue前后端不同端口的实现方法
2018/09/19 Javascript
使用angularjs.foreach时return的问题解决
2018/09/30 Javascript
图文讲解vue的v-if使用方法
2019/02/11 Javascript
了解前端理论:rscss和rsjs
2019/05/23 Javascript
微信小程序picker组件两列关联使用方式
2020/10/27 Javascript
[44:50]DOTA2上海特级锦标赛B组小组赛#2 VG VS Fnatic第二局
2016/02/26 DOTA
python中while循环语句用法简单实例
2015/05/07 Python
Python运行报错UnicodeDecodeError的解决方法
2016/06/07 Python
详解Python各大聊天系统的屏蔽脏话功能原理
2016/12/01 Python
python中实现延时回调普通函数示例代码
2017/09/08 Python
python利用dir函数查看类中所有成员函数示例代码
2017/09/08 Python
基于python中pygame模块的Linux下安装过程(详解)
2017/11/09 Python
python实现超市扫码仪计费
2018/05/30 Python
Python 获取项目根路径的代码
2019/09/27 Python
python 按钮点击关闭窗口的实现
2020/03/04 Python
django-csrf使用和禁用方式
2020/03/13 Python
python 已知三条边求三角形的角度案例
2020/04/12 Python
Python编写单元测试代码实例
2020/09/10 Python
10款最佳Python开发工具推荐,每一款都是神器
2020/10/15 Python
荷兰电脑专场:Paradigit
2018/05/05 全球购物
卡骆驰德国官方网站:Crocs德国
2019/03/29 全球购物
全球才华横溢工匠的家居装饰、珠宝和礼物:NOVICA
2021/01/22 全球购物
教育系统干部作风整顿心得体会
2014/09/09 职场文书
银行柜员与客户起冲突检讨书
2014/09/27 职场文书
win10系统计算机图标怎么调出来?win10调出计算机图标的方法
2022/08/14 数码科技