Python高級:細說Python淺拷貝和深拷貝

0.說明 python

        先看看淺拷貝的概念:
編程

  • 淺拷貝:對一個對象進行淺拷貝實際上是新建立了一個類型跟原對象同樣,其內容仍是原來對象元素的引用,換句話說,這個拷貝的對象自己是新的,可是它的內容不是ide

        序列類型對象的淺拷貝是默認類型拷貝,有如下幾種實現方式:
函數

  • 徹底切片操做:下面操做會有spa

  • 利用工廠函數:好比list()、dict()等orm

  • 使用copy模塊的copy()函數對象

        其實若是是真正理解了Python對象或者說理解了可變對象和不可變對象,再根據上面的理論知識,淺拷貝和深拷貝基本上算是比較好的掌握了。因此這裏不按照書上(指的是《Python核心編程》)的思路來進行總結,固然書上的例子做爲入門也是很是不錯的。下面給出三個例子,若是均可以理解,那麼對Python淺拷貝和深拷貝的掌握到這個程度也就能夠了。it




1.第一個例子:列表中的元素都是原子類型,即不可變對象入門

        

>>> person = ['age', 20]
>>> xpleaf = person[:]  #淺拷貝
>>> cl = list(person)      #淺拷貝
>>> [id(x) for x in person, xpleaf, cl]   #雖然是淺拷貝,可是其實也是生成了新的對象
[140205544875144, 140205544893688, 140205544996232]
>>> [id(x) for x in person]
[140205545021232, 32419728]
>>> [id(x) for x in xpleaf]
[140205545021232, 32419728]
>>> [id(x) for x in cl]
[140205545021232, 32419728]
#可是能夠看到列表中的元素的仍是原來對象元素的引用

        上面作了淺拷貝的操做,而後下面修改兩個淺拷貝的值:class

>>> xpleaf[1] = 22
>>> cl[1] = 21
>>> person, xpleaf, cl
(['age', 20], ['age', 22], ['age', 21])
>>> [id(x) for x in person]
[140205545021232, 32419728]
>>> [id(x) for x in xpleaf]
[140205545021232, 32419680]
>>> [id(x) for x in cl]
[140205545021232, 32419704]

        修改了兩個淺拷貝的值,而後發現內容並無相互影響,並且後來的id值也發生改變了,怎麼會這樣?不要忘了,列表中的元素都是不可變對象,修改不可變對象的值,其實就至關因而新生成了一個該對象,而後讓列表元素從新指向新生成的不可變對象,在這裏是數字對象。

        理解這個例子自己並不難,但須要對Python對象和序列類型自己有必定的理解。




2. 第二個例子:列表中包含容器類型變量,便可變對象

        

>>> person = ['name', ['age', 20]]
>>> xpleaf = person[:]
>>> cl = list(person)
>>> person, xpleaf, cl
(['name', ['age', 20]], ['name', ['age', 20]], ['name', ['age', 20]])
>>> [id(x) for x in person, xpleaf, cl]
[140205544995944, 140205544893688, 140205544875144]
# 查看大列表的元素id值
>>> [id(x) for x in person, xpleaf, cl]
[140205544996160, 140205544875144, 140205544996520]
>>> [id(x) for x in person]
[140205546176112, 140205544995944]
>>> [id(x) for x in xpleaf]
[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 xpleaf[1]]
[140205545021232, 32419680]
>>> [id(x) for x in cl[1]]
[140205545021232, 32419680]

        三個列表的第一個元素的id值都是同樣的,這是引用傳遞,沒有什麼問題,跟第一個例子相似,所以修改這個值不會有什麼問題。但注意看第二個元素,它是一個列表,能夠確定的是,三個列表中的兩個元素的id也確定是相同的,也是引用傳遞的道理,但如今關鍵是看第二個元素,也就是這個列表自己,三個大列表(指的是person這個列表)中的這三個小列表的id值都是同樣的,因而,淺拷貝對於對象值的影響就會體現出來了,咱們嘗試去修改其中一個小列表中的值:

>>> xpleaf[1][1] = 22
>>> person, xpleaf, cl
(['name', ['age', 22]], ['name', ['age', 22]], ['name', ['age', 22]])
>>> [id(x) for x in person, xpleaf, cl]
[140205544995944, 140205544893688, 140205544875144]
# 查看大列表的元素id值
>>> [id(x) for x in person]
[140205546176112, 140205544995944]
>>> [id(x) for x in xpleaf]
[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 xpleaf[1]]
[140205545021232, 32419680]
>>> [id(x) for x in cl[1]]
[140205545021232, 32419680]

        能夠看到問題就出來了,即對一個小列表進行修改,會影響到其它的小列表。咱們先拋開所謂的淺拷貝,去思考這個問題自己:有可能不會影響其它小列表嗎?確定沒有可能的,由於三個小列表的id都同樣,三個小列表裏的元素的id也同樣,即其實這三個小列表是徹底指向同一個對象的,所以,不管修改哪個,確定都會影響其它小列表的。

        這就是所謂淺拷貝出現的問題。




3.第三個例子:使用深拷貝來解決第二個例子出現的問題

        

>>> person = ['name', ['age', 20]]
>>> xpleaf = person[:]
>>> from copy import deepcopy as dcp
>>> cl = dcp(person)
>>> person, xpleaf, cl
(['name', ['age', 20]], ['name', ['age', 20]], ['name', ['age', 20]])
>>> [id(x) for x in person, xpleaf, cl]
[140205544995944, 140205544893688, 140205544875144]
# 查看大列表的元素id值
>>> [id(x) for x in person]
[140205546176112, 140205544996520]
>>> [id(x) for x in xpleaf]
[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 xpleaf[1]]
[140205545021232, 32419728]
>>> [id(x) for x in cl[1]]
[140205545021232, 32419728]

       能夠看到雖然是進行了深拷貝,但發現跟前面的其實並無什麼不一樣,下面咱們再來修改其中一個小列表:

>>> xpleaf[1][1] = 22
>>> person, xpleaf, cl
(['name', ['age', 22]], ['name', ['age', 22]], ['name', ['age', 20]])
# 查看大列表的元素id值
>>> [id(x) for x in person]
[140205546176112, 140205544996520]
>>> [id(x) for x in xpleaf]
[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 xpleaf[1]]
[140205545021232, 32419680]
>>> [id(x) for x in cl[1]]
[140205545021232, 32419728]

        此時能夠看到,cl的小列表的第二個元素的id跟原來是同樣的,可是xpleaf和person的小列表元素的id發生了改變,同時值也是咱們修改的那樣。那是由於xpleaf是person的淺拷貝,可是cl是person的深拷貝。

        這就是所謂的深拷貝。




4.第四個例子:檢驗


         其實只要理解了上面三個例子(這意味着對Python對象自己和序列類型自己也有比較深入的理解),因此的淺拷貝和深拷貝也不是什麼問題了。

        至因而否明白,能夠參考下面這個例子:

>>> person = ['name', ('hobby', [1, 2])]
>>> xpleaf = person[:]
>>> from copy import deepcopy as dcp
>>> cl = dcp(person)
>>> 
>>> xpleaf[0] = 'xpleaf'
>>> cl[0] = 'cl'
>>> person, xpleaf, cl
(['name', ('hobby', [1, 2])], ['xpleaf', ('hobby', [1, 2])], ['cl', ('hobby', [1, 2])])
>>> 
>>> xpleaf[1][1][0] = 'clyyh'
>>> person, xpleaf, cl
(['name', ('hobby', ['clyyh', 2])], ['xpleaf', ('hobby', ['clyyh', 2])], ['cl', ('hobby', [1, 2])])

        若是對這個例子的輸出以爲徹底沒有問題的,那麼也就OK了!



        固然,確定還有遺漏的地方,還望指出。

相關文章
相關標籤/搜索