对象的身份,相等性和别名
先看一个例子:
>>> a = [1,2,3,4]
>>> b = a
>>> b is a
True
>>> id(a),id(b)
(58065824, 58065824)
>>> a.append(5)
>>> a
[1, 2, 3, 4, 5]
>>> b
[1, 2, 3, 4, 5]
>>>
(1)这里的b就是a的一个别名(alias)
(2)所以is返回true,id()函数对a和b返回的值也是相同的
(3)针对a的修改也同时修改了b
继续:
>>> c = [1,2,3,4,5]
>>> c == a
True
>>> c is a
False
>>>
(1)这里的c的值与a相等,所以==返回True
(2)但是c不是a的别名,它们的id不同
它们在内存中的数据模型如下图所示:
为什么会有上面的不同呢?
其实,在python手册中的数据模型解释中有这么一段话:
Every object has an identity, a type and a value. An object’s identity never changes once
it has been created; you may think of it as the object’s address in memory. The is operator
compares the identity of two objects; the id() function returns an integer representing its identity.
主要意思:
(1) 每个对象都有一个唯一的id,(CPython中id就是对象在内存中的地址),且在创建的时候确定,以后不再改变;
(2) is比较是比较两个对象的id;
(3) id()函数返回的是一个整形的对象id;
上面的例子中c是一个新创建的对象,虽然内容和a一样,但是id是不同的,所以is返回的是False,而b仅仅是a的一个别名,相当于C++中的引用,b和a在内存中的地址是一样的,是同一个对象,所以is返回True。至于c和a的==比较返回True,下面会继续解释。
何时选择is,何时用==
==操作符比较的是对象的内容,而is比较的是对象的id。
==操作符在比较的时候其实是调用了对象的.eq()函数, a == b其实是调用了a.eq(b)的结果。这也就是为什么is会比==快的原因。
元组(tuple) 不变性的一个陷阱
元组(tuple)和其他容器类型一样,存放的都是对象的引用,元组是不可改变的,但是如果它的元素是可变类型的,那么它还是可以被改变的。下面的例子可以说明:
>>> t1= (1,2,[3,4])
>>> t1[-1].append(5)
>>> t1
(1, 2, [3, 4, 5])
>>>
通过Python Online Tutor可以看到这里的内存数据结构如下图:
在看如下操作,
t1[2] += [6,7]
这个会发生什么呢?
>>> t1[2] += [6,7]
Traceback (most recent call last):
File "<pyshell#25>", line 1, in <module>
t1[2] += [6,7]
TypeError: 'tuple' object does not support item assignment
>>> t1
(1, 2, [3, 4, 5, 6, 7])
可以看到,抛出了异常,这很好理解,tuple是不可变的,针对tuple元素的赋值会抛出TypeError异常。但是这里的t1中的列表元素还是变了,再看下执行前后的状态。