详解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通过定义一个类实例作为ftp回调方法
May 04 Python
python数据结构链表之单向链表(实例讲解)
Jul 25 Python
基于Python的XSS测试工具XSStrike使用方法
Jul 29 Python
python实现猜数字小游戏
Mar 24 Python
Django的CVB实例详解
Feb 10 Python
pandas之分组groupby()的使用整理与总结
Jun 18 Python
appium+python自动化配置(adk、jdk、node.js)
Nov 17 Python
OpenCV+Python3.5 简易手势识别的实现
Dec 21 Python
python实现跨年表白神器--你值得拥有
Jan 04 Python
使用Python+Appuim 清理微信的方法
Jan 26 Python
selenium.webdriver中add_argument方法常用参数表
Apr 08 Python
python单向链表实例详解
May 25 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 数组动态添加实现代码(最土团购系统的价格排序)
2011/12/30 PHP
实现在同一方法中获取当前方法中新赋值的session值解决方法
2014/06/26 PHP
php获取YouTube视频信息的方法
2015/02/11 PHP
php版微信js-sdk支付接口类用法示例
2016/10/12 PHP
预加载css或javascript的js代码
2010/04/23 Javascript
jquery获取div宽度的实现思路与代码
2013/01/13 Javascript
利用jQuary实现文字浮动提示效果示例代码
2013/12/26 Javascript
Bootstrap每天必学之按钮
2015/11/26 Javascript
Bootstrap每天必学之模态框(Modal)插件
2016/04/26 Javascript
Jquery对新插入的节点 绑定Click事件失效的解决方法
2016/06/02 Javascript
javascript类型系统——日期Date对象全面了解
2016/07/13 Javascript
详解vue-cli + webpack 多页面实例配置优化方法
2017/07/13 Javascript
React-Native做一个文本输入框组件的实现代码
2017/08/10 Javascript
vuex学习之Actions的用法详解
2017/08/29 Javascript
页面点击小红心js实现代码
2018/05/26 Javascript
ajaxfileupload.js实现上传文件功能
2019/04/19 Javascript
详解微信小程序-canvas绘制文字实现自动换行
2019/04/26 Javascript
微信小程序实现文件、图片上传功能
2020/08/18 Javascript
JS常用排序方法实例代码解析
2020/03/03 Javascript
关于vue的列表图片选中打钩操作
2020/09/09 Javascript
Python中使用HTMLParser解析html实例
2015/02/08 Python
Python中easy_install 和 pip 的安装及使用
2017/06/05 Python
python+selenium实现简历自动刷新的示例代码
2019/05/20 Python
Django框架视图介绍与使用详解
2019/07/18 Python
Python利用逻辑回归模型解决MNIST手写数字识别问题详解
2020/01/14 Python
谈一谈数组拼接tf.concat()和np.concatenate()的区别
2020/02/07 Python
Pytorch中.new()的作用详解
2020/02/18 Python
基于FME使用Python过程图解
2020/05/13 Python
HTML5 移动页面自适应手机屏幕四类方法总结
2017/08/17 HTML / CSS
超30万乐谱下载:Musicnotes.com
2016/09/24 全球购物
英国顶尖手表珠宝品牌独家授权经销商:HS Johnson
2020/10/28 全球购物
Python使用openpyxl复制整张sheet
2021/03/24 Python
军校制空专业毕业生自我鉴定
2013/11/16 职场文书
公共机构节能宣传周活动总结
2014/07/09 职场文书
课外访万家心得体会
2014/09/03 职场文书
工作疏忽、懈怠的检讨书
2014/09/11 职场文书