目錄
1.回顧
在前面的課中,已經學許多python對象比較和複製的例子
if a == b:
...
這裏的l2就是l1拷貝得到的。
l1 = [1, 2, 3]
l2 = list(l1)
l2是l1的淺拷貝(shallow copy)還是深拷貝(deep copy)呢?
a == b是比較兩個對象的值相等,還是兩個對象完全相等呢?
1.“==”VS“is”
== 和is是python中對象比較常用的兩種方式,“==”操作符比較對象之間的值是否相等,eg
a == b
而is 操作符比較是對象的身份標識是否相等,即它們是否是同一個對象,是否指向同一個內存地址。
在python中,每個對象的身份標識,都是通過函數id(object)獲得。因此,'is'操作符,相當於比較對象之間的ID是否相等,下面的例子:
a = 10
b = 10
a == b
True
id(a)
4427562448
id(b)
4427562448
a is b
True
python會給10開闢一個內存,然後變量a和b同時指向這地位內存區域,即a和b都是指向10這個變量,因此,a和b的值相等,id也相等,a==b,和a is b都返回True.
不過要注意的是,對於整型數字來說,以上 a is b爲True的結論,只適於-5到255範圍內的數字。
a = 257
b = 257
a == b
True
id(a)
4473417552
id(b)
4473417584
a is b
False
if a is None:
...
if a is not None:
...
比較操作符'is'通常優於“==”.因爲is操作符不能重載,這樣,python就不需要去尋找,程序中是否有其他重載了比較操作符,並去調用,執行比較操作符'is'就僅是比較兩個變量的ID而已。
但是“==”操作符不同,執行a ==b 相當於去執行a.__eq__(b),而python大部分的數據類型會去重載__eq__這個函數,其內部的處理會複雜一些。對於列表,__eq__函數會去遍歷列表中的元素,比較它們的順序和值是否相等。
對於不可變的變量,如果我們之前用'=='或'is‘比較過,結果是不是就上直不變了呢?
t1 = (1, 2, [3, 4])
t2 = (1, 2, [3, 4])
t1 == t2
True
t1[-1].append(5)
t1 == t2
False
答案是否定的,我們知道元組是不可變的,但元組可以嵌套,它裏面的元素可以是列表類型,列表是可變的,所以如果我們修改了元組中的某個可變元素,那麼元組本身也就改變了,之前用'is'或是'=='操作符取得結果,可能就不適用。
2.淺拷貝和深度拷貝
常見的淺拷貝方法,是使用數據類型本身的構造器,比如:
l1 = [1, 2, 3]
l2 = list(l1)
l2
[1, 2, 3]
l1 == l2
True
l1 is l2
False
s1 = set([1, 2, 3])
s2 = set(s1)
s2
{1, 2, 3}
s1 == s2
True
s1 is s2
False
這裏l2就是l1的淺拷貝,s2是s1的淺拷貝,對於可變的序列,我們還可以通過切片操作符":"完成淺拷貝,如下
l1 = [1, 2, 3]
l2 = l1[:]
l1 == l2
True
l1 is l2
False
當然,python中也提供了相對應的函數copy.copy(),適用於任何數據類型:
import copy
l1 = [1, 2, 3]
l2 = copy.copy(l1)
不過,需要注意,對於元組不講,使用tuple()或是切片操作符':'不會創建一份淺拷貝,相反,它會返回一個指向相同元組的引用
t1 = (1, 2, 3)
t2 = tuple(t1)
t1 == t2
True
t1 is t2
True
這裏,元組(1,2,3)只被創建一次,t1和t2同時指向這個元組。淺拷貝就是重新分配一塊內存,創建上個新對對象裏面的元素是原對象中子對象的引用,因此,如果原對象中的元互素不變,那無所謂,但變了,淺拷貝會的副作用,所以要注意。
l1 = [[1, 2], (30, 40)]
l2 = list(l1)
l1.append(100)
l1[0].append(3)
l1
[[1, 2, 3], (30, 40), 100]
l2
[[1, 2, 3], (30, 40)]
l1[1] += (50, 60)
l1
[[1, 2, 3], (30, 40, 50, 60), 100]
l2
[[1, 2, 3], (30, 40)]
首先初始化一個列表l1,裏面的元素是一個列表和一個元組,然後對l1執行淺拷貝,賦予l2.因爲淺拷貝里的元素是對原元素的引用,因此l2中的元素和l1指向同一個列表和元組對象。
2.深拷貝
python中copy.deepcopy()來實現對象的深度拷貝
import copy
l1 = [[1, 2], (30, 40)]
l2 = copy.deepcopy(l1)
l1.append(100)
l1[0].append(3)
l1
[[1, 2, 3], (30, 40), 100]
l2
[[1, 2], (30, 40)]
無論l1如何變,l2不會變。是獨立的。
不過深拷貝也不完美,如果拷貝對象中存在指向自身的引用,那麼會陷入無限循環。
import copy
x = [1]
x.append(x)
x
[1, [...]]
y = copy.deepcopy(x)
y
[1, [...]]
3.總結
4.思考
跑一下代碼,檢驗自己的想法
import copy
x = [1]
x.append(x)
y = copy.deepcopy(x)
# 以下命令的輸出是?
x == y
淺拷貝,不可變的不可變,可變的依舊可變
深拷貝,都不可變