0.說明
先看看淺拷貝的概念:
淺拷貝:對一個對象進行淺拷貝其實是新創建了一個類型跟原對象一樣,其內容還是原來對象元素的引用,換句話說,這個拷貝的對象本身是新的,但是它的內容不是序列類型對象的淺拷貝是默認類型拷貝,有以下幾種實現方式:
完全切片操作:下面操作會有利用工廠函數:比如list()、dict()等
使用copy模塊的copy()函數
其實如果是真正理解了Python對象或者說理解了可變對象和不可變對象,再根據上面的理論知識,淺拷貝和深拷貝基本上算是比較好的掌握了。所以這裡不按照書上(指的是《Python核心編程》)的思路來進行總結,當然書上的例子作為入門也是非常不錯的。下面給出三個例子,如果都可以理解,那麼對Python淺拷貝和深拷貝的掌握到這個程度也就可以了。
--------------------------------------------------------------------------------
1.第一個例子:列表中的元素都是原子類型,即不可變對象
>>> person = ['age', 20]
>>> linuxidc = person[:] #淺拷貝
>>> cl = list(person) #淺拷貝
>>> [id(x) for x in person, linuxidc, cl] #雖然是淺拷貝,但是其實也是生成了新的對象
[140205544875144, 140205544893688, 140205544996232]
>>> [id(x) for x in person]
[140205545021232, 32419728]
>>> [id(x) for x in linuxidc]
[140205545021232, 32419728]
>>> [id(x) for x in cl]
[140205545021232, 32419728]
#但是可以看到列表中的元素的還是原來對象元素的引用
上面做了淺拷貝的操作,然後下面修改兩個淺拷貝的值:
>>> linuxidc[1] = 22
>>> cl[1] = 21
>>> person, linuxidc, cl
(['age', 20], ['age', 22], ['age', 21])
>>> [id(x) for x in person]
[140205545021232, 32419728]
>>> [id(x) for x in linuxidc]
[140205545021232, 32419680]
>>> [id(x) for x in cl]
[140205545021232, 32419704]
修改了兩個淺拷貝的值,然後發現內容並沒有相互影響,而且後來的id值也發生改變了,怎麼會這樣?不要忘了,列表中的元素都是不可變對象,修改不可變對象的值,其實就相當於是新生成了一個該對象,然後讓列表元素重新指向新生成的不可變對象,在這裡是數字對象。
理解這個例子本身並不難,但需要對Python對象和序列類型本身有一定的理解。
--------------------------------------------------------------------------------
2. 第二個例子:列表中包含容器類型變量,即可變對象
>>> person = ['name', ['age', 20]]
>>> linuxidc = person[:]
>>> cl = list(person)
>>> person, linuxidc, cl
(['name', ['age', 20]], ['name', ['age', 20]], ['name', ['age', 20]])
>>> [id(x) for x in person, linuxidc, cl]
[140205544995944, 140205544893688, 140205544875144]
# 查看大列表的元素id值
>>> [id(x) for x in person, linuxidc, cl]
[140205544996160, 140205544875144, 140205544996520]
>>> [id(x) for x in person]
[140205546176112, 140205544995944]
>>> [id(x) for x in linuxidc]
[140205546176112, 140205544995944]
>>> [id(x) for x in cl]
[140205546176112, 140205544995944]
# 查看小列表的元素id值
>>> [id(x) for x in person[1]]
[140205545021232, 32419680]
>>> [id(x) for x in linuxidc[1]]
[140205545021232, 32419680]
>>> [id(x) for x in cl[1]]
[140205545021232, 32419680]
三個列表的第一個元素的id值都是一樣的,這是引用傳遞,沒有什麼問題,跟第一個例子類似,因此修改這個值不會有什麼問題。但注意看第二個元素,它是一個列表,可以肯定的是,三個列表中的兩個元素的id也肯定是相同的,也是引用傳遞的道理,但現在關鍵是看第二個元素,也就是這個列表本身,三個大列表(指的是person這個列表)中的這三個小列表的id值都是一樣的,於是,淺拷貝對於對象值的影響就會體現出來了,我們嘗試去修改其中一個小列表中的值:
>>> linuxidc[1][1] = 22
>>> person, linuxidc, cl
(['name', ['age', 22]], ['name', ['age', 22]], ['name', ['age', 22]])
>>> [id(x) for x in person, linuxidc, cl]
[140205544995944, 140205544893688, 140205544875144]
# 查看大列表的元素id值
>>> [id(x) for x in person]
[140205546176112, 140205544995944]
>>> [id(x) for x in linuxidc]
[140205546176112, 140205544995944]
>>> [id(x) for x in cl]
[140205546176112, 140205544995944]
# 查看小列表的元素id值
>>> [id(x) for x in person[1]]
[140205545021232, 32419680]
>>> [id(x) for x in linuxidc[1]]
[140205545021232, 32419680]
>>> [id(x) for x in cl[1]]
[140205545021232, 32419680]
可以看到問題就出來了,即對一個小列表進行修改,會影響到其它的小列表。我們先拋開所謂的淺拷貝,去思考這個問題本身:有可能不會影響其它小列表嗎?肯定沒有可能的,因為三個小列表的id都一樣,三個小列表裡的元素的id也一樣,即其實這三個小列表是完全指向同一個對象的,因此,無論修改哪一個,肯定都會影響其它小列表的。
這就是所謂淺拷貝出現的問題。
--------------------------------------------------------------------------------
3.第三個例子:使用深拷貝來解決第二個例子出現的問題
>>> person = ['name', ['age', 20]]
>>> linuxidc = person[:]
>>> from copy import deepcopy as dcp
>>> cl = dcp(person)
>>> person, linuxidc, cl
(['name', ['age', 20]], ['name', ['age', 20]], ['name', ['age', 20]])
>>> [id(x) for x in person, linuxidc, cl]
[140205544995944, 140205544893688, 140205544875144]
# 查看大列表的元素id值
>>> [id(x) for x in person]
[140205546176112, 140205544996520]
>>> [id(x) for x in linuxidc]
[140205546176112, 140205544996520]
>>> [id(x) for x in cl]
[140205546176112, 140205544571320]
# 查看小列表的元素id值
>>> [id(x) for x in person[1]]
[140205545021232, 32419728]
>>> [id(x) for x in linuxidc[1]]
[140205545021232, 32419728]
>>> [id(x) for x in cl[1]]
[140205545021232, 32419728]
可以看到雖然是進行了深拷貝,但發現跟前面的其實並沒有什麼不同,下面我們再來修改其中一個小列表:
>>> linuxidc[1][1] = 22
>>> person, linuxidc, cl
(['name', ['age', 22]], ['name', ['age', 22]], ['name', ['age', 20]])
# 查看大列表的元素id值
>>> [id(x) for x in person]
[140205546176112, 140205544996520]
>>> [id(x) for x in linuxidc]
[140205546176112, 140205544996520]
>>> [id(x) for x in cl]
[140205546176112, 140205544571320]
# 查看小列表的元素id值
>>> [id(x) for x in person[1]]
[140205545021232, 32419680]
>>> [id(x) for x in linuxidc[1]]
[140205545021232, 32419680]
>>> [id(x) for x in cl[1]]
[140205545021232, 32419728]
此時可以看到,cl的小列表的第二個元素的id跟原來是一樣的,但是linuxidc和person的小列表元素的id發生了改變,同時值也是我們修改的那樣。那是因為linuxidc是person的淺拷貝,但是cl是person的深拷貝。
這就是所謂的深拷貝。
--------------------------------------------------------------------------------
4.第四個例子:檢驗
其實只要理解了上面三個例子(這意味著對Python對象本身和序列類型本身也有比較深刻的理解),所以的淺拷貝和深拷貝也不是什麼問題了。
至於是否明白,可以參考下面這個例子:
>>> person = ['name', ('hobby', [1, 2])]
>>> linuxidc = person[:]
>>> from copy import deepcopy as dcp
>>> cl = dcp(person)
>>>
>>> linuxidc[0] = 'xpleaf'
>>> cl[0] = 'cl'
>>> person, linuxidc, cl
(['name', ('hobby', [1, 2])], ['linuxidc', ('hobby', [1, 2])], ['cl', ('hobby', [1, 2])])
>>>
>>> linuxidc[1][1][0] = 'clyyh'
>>> person, linuxidc, cl
(['name', ('hobby', ['clyyh', 2])], ['linuxidc', ('hobby', ['clyyh', 2])], ['cl', ('hobby', [1, 2])])
如果對這個例子的輸出覺得完全沒有問題的,那麼也就OK了!
當然,肯定還有遺漏的地方,還望指出。
下面關於Python的文章您也可能喜歡,不妨看看:
Linux下Python的安裝以及注意事項 http://www.linuxidc.com/Linux/2015-11/124861.htm
Ubuntu 14.04 下安裝使用Python rq模塊 http://www.linuxidc.com/Linux/2015-08/122441.htm
無需操作系統直接運行 Python 代碼 http://www.linuxidc.com/Linux/2015-05/117357.htm
CentOS上源碼安裝Python3.4 http://www.linuxidc.com/Linux/2015-01/111870.htm
《Python核心編程 第二版》.(Wesley J. Chun ).[高清PDF中文版] http://www.linuxidc.com/Linux/2013-06/85425.htm
《Python開發技術詳解》.( 周偉,宗傑).[高清PDF掃描版+隨書視頻+代碼] http://www.linuxidc.com/Linux/2013-11/92693.htm
Python腳本獲取Linux系統信息 http://www.linuxidc.com/Linux/2013-08/88531.htm
在Ubuntu下用Python搭建桌面算法交易研究環境 http://www.linuxidc.com/Linux/2013-11/92534.htm
Python 語言的發展簡史 http://www.linuxidc.com/Linux/2014-09/107206.htm
Python 的詳細介紹:請點這裡
Python 的下載地址:請點這裡