賦值(assignment)
在Python中,用一個變量給另一個變量賦值,其實就是給當前內存中的對象增加一個“標籤”而已。
看下面的例子:
>>> a = [6, 6, 6]
>>> b = a
>>> print(id(a), id(b), sep = '\n')
4492424448
4492424448
可以看出,其實a和b指向內存中同一個對象。
>>> a is b
True
淺拷貝(shallow copy)
淺拷貝是指創建一個新的對象,其內容是原對象中元素的引用(新對象與原對象共享內存中的子對象)。
注意: 淺拷貝和深拷貝的不同僅僅是對組合對象來說,所謂的組合對象就是包含了其他對象的對象,如列表,類實例等等。而對於數字、字符串以及其他“原子”類型,沒有拷貝一說,產生的都是原對象的引用。
常見的淺拷貝有:切片操作、工廠函數、對象的copy()方法,copy模塊中的copy函數。
看下面的例子:
>>> a = [6, 8, 9]
>>> b = list(a)
>>> print(id(a), id(b))
4493469248 4493592128 #a和b的地址不同
>>> for x, y in zip(a, b):
... print(id(x), id(y))
...
4489786672 4489786672
4489786736 4489786736
4489786768 4489786768
# 但是他們的子對象地址相同
從上面的例子中可以看出,a淺拷貝得到b,a和b指向內存中不同的list對象,但是他們的元素指向相同的int對象,這就是淺拷貝。
深拷貝(deep copy)
深拷貝是指創建一個新的對象,然後遞歸的拷貝原對象所包含的子對象。深拷貝出來的對象與原對象沒有任何關聯。
深拷貝只有一種方式:copy模塊中的deepcopy函數。
>>> import copy
>>> a = [6, 6, 6]
>>> b = copy.deepcopy(a)
>>> print(id(a), id(b))
4493468448 4493469248
>>> for x, y in zip(a, b):
... print(id(x), id(y))
...
4489786672 4489786672
4489786672 4489786672
4489786672 4489786672
上面的例子是一個特殊情況,爲什麼使用了深拷貝,但是子對象的地址還是一樣呢?這是因爲對於不可變對象,當需要一個新的對象時,Python可能會返回已經存在的某個類型和值一致的對象的引用。而且這種機制並不會影響a和b的相互獨立性,因爲當兩個元素指向同一個不可變對象時,對其中一個賦值不會影響另外一個。
我們接下來用一個包含可變對象的列表來確切地展示淺拷貝和深拷貝的區別:
>>> a = [[6, 6], [8, 8], [9, 9]]
>>> b = copy.copy(a) # 淺拷貝
>>> c = copy.deepcopy(a) # 深拷貝
>>> print(id(a), id(b)) # a和b地址不同
4493780304 4494523680
>>> for x, y in zip(a, b): # a和b的子對象地址相同
... print(id(x), id(y))
...
4493592128 4493592128
4494528592 4494528592
4493779024 4493779024
>>> print(id(a), id(c)) # a和c不同
4493780304 4493469248
>>> for x, y in zip(a, c): # a和c的子對象地址也不同
... print(id(x), id(y))
...
4493592128 4493687696
4494528592 4493686336
4493779024 4493684896