作者:刘志军,6年+Python使用经验, 高级开发工程师,目前在互联网医疗行业从事Web系统构架工作
个人公众号:Python之禅(微信ID:vttalk)
题图:unsplash.com by Dmitry Pavlov
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。
可变对象(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
执行append操作后,对象的内存地址不会改变,x、y 依然指向的是原来同一个对象,只不过是他的值发生了变化而已。
理解完可变对象与不可变对象后,回到问题本身,
+=
与
+
的区别在哪里呢?
+=
操作首先会尝试调用对象的
__iadd__
方法,如果没有该方法,那么尝试调用
__add__
方法
,先来看看这两个方法有什么区别
__add__和
__
iadd
__
的区别
>>> hasattr(int, '__iadd__')
False
>>> hasattr(list, '__iadd__')
True