首先,簡單理解一下概念:(注意:以下概念都是建立在可變數據類型上,包括列表list和字典dict)
1、直接賦值:當創建一個對象a,然后把它賦給另一個變量b的時候,python並沒有拷貝這個對象,而只是拷貝了這個對象的引用;原始列表改變,被賦值的b也會做相同的改變;
2、copy淺拷貝,只拷貝了父對象,沒有拷貝子對象,所以原始數據的子對象的改變,淺拷貝的子對象也會改變;但是父對象的改變,淺拷貝的父對象不會改變;
3、深拷貝,包含父對象里面的子對象的拷貝,所以原始對象的改變不會造成深拷貝里任何子元素的改變;
接下來我們來跑一個代碼例子,原始對象包含子對象的例子
import copy# 列表型,即可變數據類型,但不包含子對象
a=[1,2] b=copy.deepcopy(a) c=a d=copy.copy(a) e=[1,2] print(b is a) print(c is a) print(d is a) print(e is a) print("變化前:",a,b,c,d,e,"\n") a.append(5) print("變化后:",a,b,c,d,e,"\n")
輸出結果:
False True False False 變化前: [1, 2] [1, 2] [1, 2] [1, 2] [1, 2]
變化后: [1, 2, 5] [1, 2] [1, 2, 5] [1, 2] [1, 2]
分析:
1、我們創建一個列表型數據a,即可變數據類型,但不包含子對象;
PS:我們使用身份運算符is,判斷兩個標識符是不是引用自一個對象
x is y
, 類似 id(x) == id(y)
, 如果引用的是同一個對象則返回 True,否則返回 False
2、b深拷貝a,與a不是引用自一個對象,所以輸出false
3、c是a直接賦值的,拷貝了a的引用,即與a引用自同一個對象,所以輸出true
4、d淺拷貝a,與a也不是引用自同一個對象,所以輸出false
5、e雖然和a值一樣,但屬於不同的對象,在內存中有不一樣的地址,所以輸出false
6、a對象增加值5,只有直接賦值的c的值同步發生了改變,因為深拷貝和淺拷貝的父對象和原始對象都是不一樣的了(可以回頭看看概念和圖例),而e對象本來就是與a無關的,所以更加不會發生變化。
我們再跑一個代碼例子,原始對象包含子對象的例子
import copy
# 列表型,即不可變數據類型,但包含子對象
a=[1,2,[11,22]] b=copy.deepcopy(a) c=a d=copy.copy(a) e=[1,2,[11,22]] print(b is a) print(c is a) print(d is a) print(e is a) print("變化前:",a,b,c,d,e,"\n") a[2].append(33) print("變化后:",a,b,c,d,e,"\n")
輸出結果:
False True False False 變化前: [1, 2, [11, 22]] [1, 2, [11, 22]] [1, 2, [11, 22]] [1, 2, [11, 22]] [1, 2, [11, 22]] 變化后: [1, 2, [11, 22, 33]] [1, 2, [11, 22]] [1, 2, [11, 22, 33]] [1, 2, [11, 22, 33]] [1, 2, [11, 22]]
分析:
1、我們創建一個列表型數據a,即可變數據類型,且包含子對象,子對象也是個列表;
PS:我們使用身份運算符is,判斷兩個標識符是不是引用自一個對象
x is y
, 類似 id(x) == id(y)
, 如果引用的是同一個對象則返回 True,否則返回 False
2、b深拷貝a,與a不是引用自一個對象,所以輸出false
3、c是a直接賦值的,拷貝了a的引用,即與a引用自同一個對象,所以輸出true
4、d淺拷貝a,與a也不是引用自同一個對象,所以輸出false
5、e雖然和a值一樣,但屬於不同的對象,在內存中有不一樣的地址,所以輸出false
6、a對象的子對象列表增加值33,除了直接賦值的c的值同步發生了改變,淺拷貝的子對象也發生了改變,因為淺拷貝的子對象和原始對象中的子對象引用的是同一個對象!(可以回頭看看概念和圖例),而e對象本來就是與a無關的,所以依然不會發生變化。
最后,我們來思考一個問題,如果是不可變數據類型,結果是什么?
import copy # 數值型,即不可變數據類型
a=1 b=copy.deepcopy(a) c=a d=copy.copy(a) e=1
print(b is a) print(c is a) print(d is a) print(e is a) print("變化前:",a,b,c,d,e,"\n") a=2
print("變化后:",a,b,c,d,e,"\n")
輸出結果:
True True True True 變化前: 1 1 1 1 1 變化后: 2 1 1 1 1
分析:
1、我們創建一個數值型數據a,即不可變數據類型;
PS:我們使用身份運算符is,判斷兩個標識符是不是引用自一個對象
x is y
, 類似 id(x) == id(y)
, 如果引用的是同一個對象則返回 True,否則返回 False
2、結果發現無論是深拷貝、淺拷貝、直接賦值還是新建一個數值一樣的變量,在內存中的地址都是一樣的,身份運算輸出都是true。這是因為:不可變數據類型,不允許變量的值發生變化,如果改變了變量的值,相當於是新建了一個對象,而對於相同的值的對象,在內存中則只有一個對象(一個地址)!
6、所以當a變量的值改變之后,其他的變量都不會改變,因為是a引用的對象變了,而其他變量的引用並沒有發生改變,依然引用“數值1”這個對象。
由此可見,對於不可變數據類型(數值型、字符串型和元組),拷貝都是無差別的。