python物件的引用特徵和可變性
物件的身份,相等性和別名
先看一個例子:
>>> 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
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])
>>>
在看如下操作,
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中的列表元素還是變了,再看下執行前後的狀態。