1. 程式人生 > >Python中copy和deepcopy方法的區別

Python中copy和deepcopy方法的區別

copy()與deepcopy()之間的主要區別是python對資料的儲存方式。

首先直接上結論:

—–深複製,即將被複制物件完全再複製一遍作為獨立的新個體單獨存在。所以改變原有被複制物件不會對已經複製出來的新物件產生影響。 

—–而等於賦值,並不會產生一個獨立的物件單獨存在,他只是將原有的資料塊打上一個新標籤,所以當其中一個標籤被改變的時候,資料塊就會發生變化,另一個標籤也會隨之改變。

—–而淺複製要分兩種情況進行討論:

1)當淺複製的值是不可變物件(數值,字串,元組)和“等於賦值”的情況一樣,物件的id值與淺複製原來的值相同。

2)當淺複製的值是可變物件(列表和元組)時會產生一個“

不是那麼獨立的物件”存在。有兩種情況:

第一種情況:複製的 物件中無 複雜 子物件,原來值的改變並不會影響淺複製的值,同時淺複製的值改變也並不會影響原來的值。原來值的id值與淺複製原來的值不同。

第二種情況複製的物件中有 複雜 子物件 (例如列表中的一個子元素是一個列表),如果不改變其中複雜子物件淺複製的值改變並不會影響原來的值。 但是改變原來的值 中的複雜子物件的值  會影響淺複製的值

對於簡單的 object,例如不可變物件(數值,字串,元組),用 shallow copy 和 deep copy 沒區別

複雜的 object, 如 list 中套著 list 的情況,shallow copy 中的 子list,並未從原 object 真的「獨立」出來。也就是說,如果你改變原 object 的子 list 中的一個元素,你的 copy 就會跟著一起變。

這跟我們直覺上對「複製」的理解不同。

當淺複製的值是不可變物件(數值,字串,元組)時,程式碼如下:

  1. >>> a="1234567"

  2. >>> b=a

  3. >>> id(a)

  4. 4367619440

  5. >>> id(b)

  6. 4367619440

  7. >>> c=copy.copy(a)

  8. >>> id(c)

  9. 4367619440

  10. >>> d=copy.deepcopy(a)

  11. >>> id(d)

  12. 4367619440

淺複製的值是可變物件(列表,字典)時,改變的值不是 複雜子物件 程式碼如下:

  1. >>> l1=[1,2,3]

  2. >>> l2=l1

  3. >>> l3=copy.copy(l1)

  4. >>> id(l1)

  5. 4367654664

  6. >>> id(l2)

  7. 4367654664

  8. >>> id(l3)

  9. 4367628616

  10. >>> l1.append(55)

  11. >>> print(l1)

  12. [1, 2, 3, 55]

  13. >>> print(l3)

  14. [1, 2, 3]

當淺複製的值是可變物件(列表,字典)時,改變的值是 複雜子物件 程式碼如下:

  1. >>> import copy

  2. >>> list1=[1,2,['a','b']]

  3. >>> list2=list1

  4. >>> list3=copy.copy(list2)

  5. >>> list4=copy.deepcopy(list3)

  6. >>> id(list1)

  7. 4338414656

  8. >>> id(list2)

  9. 4338414656

  10. >>> id(list3)

  11. 4338415016

  12. >>> id(list4)

  13. 4338414368

  14. >>> list1[2].append('a')

  15. >>> id(list1)

  16. 4338414656

  17. >>> print list1

  18. [1, 2, ['a', 'b', 'a']]

  19. >>> print list3

  20. [1, 2, ['a', 'b', 'a']]

  21. >>> print list4

  22. [1, 2, ['a', 'b']]

  23. >>> list1.append(33)

  24. >>> id(list1)

  25. 4338414656

  26. >>> id(list3)

  27. 4338415016

  28. >>> print list1

  29. [1, 2, ['a', 'b', 'a'], 33]

  30. >>> print list3

  31. [1, 2, ['a', 'b', 'a']]


程式碼說明:當改變 複雜子物件中的元素時,淺複製值發生了變化; 當改變的值不是複雜子物件,淺複製的值沒有發生變化。因為 淺複製 ,複雜子物件的儲存方式是 作為 引用 方式儲存的,所以修改 淺複製的值 和原來的值都可以 改變 複雜子物件的值。

python的資料儲存方式

Python 儲存變數的方法跟其他 OOP 語言不同。它與其說是把值賦給變數,不如說是給變數建立了一個到具體值的 reference。

當在 Python 中 a = something 應該理解為給 something 貼上了一個標籤 a。當再賦值給 a 的時候,就好象把 a 這個標籤從原來的 something 上拿下來,貼到其他物件上,建立新的 reference。 這就解釋了一些 Python 中可能遇到的詭異情況:

  1. >> a = [1, 2, 3]

  2. >>> b = a

  3. >>> a = [4, 5, 6] //賦新的值給 a

  4. >>> a

  5. [4, 5, 6]

  6. >>> b

  7. [1, 2, 3]

  8. # a 的值改變後,b 並沒有隨著 a 變

  9. >>> a = [1, 2, 3]

  10. >>> b = a

  11. >>> a[0], a[1], a[2] = 4, 5, 6 //改變原來 list 中的元素

  12. >>> a

  13. [4, 5, 6]

  14. >>> b

  15. [4, 5, 6]

  16. # a 的值改變後,b 隨著 a 變了

上面兩段程式碼中,a 的值都發生了變化。區別在於,第一段程式碼中是直接賦給了 a 新的值(從 [1, 2, 3] 變為 [4, 5, 6]);而第二段則是把 list 中每個元素分別改變。而對 b 的影響則是不同的,一個沒有讓 b 的值發生改變,另一個變了。怎麼用上邊的道理來解釋這個詭異的不同呢?首次把 [1, 2, 3] 看成一個物品。a = [1, 2, 3] 就相當於給這個物品上貼上 a 這個標籤。而 b = a 就是給這個物品又貼上了一個 b 的標籤。 

這裡寫圖片描述 
第一種情況:a = [4, 5, 6] 就相當於把 a 標籤從 [1 ,2, 3] 上撕下來,貼到了 [4, 5, 6] 上。在這個過程中,[1, 2, 3] 這個物品並沒有消失。 b 自始至終都好好的貼在 [1, 2, 3] 上,既然這個 reference 也沒有改變過。 b 的值自然不變。

這裡寫圖片描述 
 

第二種情況:a[0], a[1], a[2] = 4, 5, 6 則是直接改變了 [1, 2, 3] 這個物品本身。把它內部的每一部分都重新改裝了一下。內部改裝完畢後,[1, 2, 3] 本身變成了 [4, 5, 6]。而在此過程當中,a 和 b 都沒有動,他們還貼在那個物品上。因此自然 a b 的值都變成了 [4, 5, 6]。

搞明白這個之後就要問了,對於一個複雜物件的淺copy,在copy的時候到底發生了什麼? 
再看一段程式碼:

  1. >>> import copy

  2. >>> origin = [1, 2, [3, 4]]

  3. #origin 裡邊有三個元素:1, 2,[3, 4]

  4. >>> cop1 = copy.copy(origin)

  5. >>> cop2 = copy.deepcopy(origin)

  6. >>> cop1 == cop2

  7. True

  8. >>> cop1 is cop2

  9. False

  10. #cop1 和 cop2 看上去相同,但已不再是同一個object

  11. >>> origin[2][0] = "hey!"

  12. >>> origin

  13. [1, 2, ['hey!', 4]]

  14. >>> cop1

  15. [1, 2, ['hey!', 4]]

  16. >>> cop2

  17. [1, 2, [3, 4]]

  18. #把origin內的子list [3, 4] 改掉了一個元素,觀察 cop1 和 cop2

學過docker的人應該對映象這個概念不陌生,我們可以把映象的概念套用在copy上面。

copy概念圖如下: 
這裡寫圖片描述

copy(淺複製)對於一個複雜物件的子物件並不會完全複製,什麼是複雜物件的子物件呢?就比如序列裡的巢狀序列字典裡的巢狀序列等都是複雜物件的子物件。對於子物件,python會把它當作一個公共映象儲存起來,所有對他的複製都被當成一個引用,所以說當其中一個引用將映象改變了之後另一個引用使用映象的時候映象已經被改變了

所以說看這裡的origin[2],也就是 [3, 4] 這個 list。根據 shallow copy 的定義,在 cop1[2] 指向的是同一個 list [3, 4]。那麼,如果這裡我們改變了這個 list,就會導致 origin 和 cop1 同時改變。這就是為什麼上邊 origin[2][0] = “hey!” 之後,cop1 也隨之變成了 [1, 2, [‘hey!’, 4]]。

deepcopy概念圖如下: 
這裡寫圖片描述

deepcopy的時候會將複雜物件的每一層複製一個單獨的個體出來。 
這時候的 origin[2] 和 cop2[2] 雖然值都等於 [3, 4],但已經不是同一個 list了。即我們尋常意義上的複製。

相關推薦

Pythoncopydeepcopy方法區別

copy()與deepcopy()之間的主要區別是python對資料的儲存方式。 首先直接上結論: —–深複製,即將被複制物件完全再複製一遍作為獨立的新個體單獨存在。所以改變原有被複制物件不會對已經複製出來的新物件產生影響。  —–而等於賦值,並不會產生一個獨立的物件

Python copydeepcopy區別

deepcopy 改變 般的 blog 等於 div copy clas 但是 import copy 假設在python中對list的復制 copy稱為淺復制 deepcopy稱為深復制 淺復制和深復制在一般的list中作用是相同的,都是進行一個復制 但是在list嵌套l

Pythoncopydeepcopy區別

copy()和deepcopy()是Python語言copy模組中的兩個method,copy()其實是與deep copy相對的shallow copy。對於簡單的object,用shallow copy和deep copy沒區別。複雜的Object,如list中套著lis

Pythoncopydeepcopy區別,以及程式設計實現deepcopy的功能

在python中,有淺複製copy()和深度複製deepcopy(),這兩個的區別我用以下的例子來說明,例如:numlist=[[1,2,[3,4,5]],34,"haha",{"name":{"fistname":"孫","lastName":"悟空"}}]設  numl

Python的深複製淺複製(等號賦值、copydeepcopy區別

(作者:陳玓玏) 不是那麼有耐心的朋友可以直接看總結! 一、深複製淺複製的區別 以我比較愚笨的理解,淺複製就是,僅複製物件的引用,而不新開闢記憶體,也就是說,會共享指標,當你改變複製後的物件時,其實是在改變原物件記憶體中的內容。 而深複製的意思是,會

Python的引用,copydeepcopy區別

Python中的引用,個人理解,與C中對地址賦值(僅針對於簡單物件,即不可修改的物件,例如int)相似。 簡單的例子: a = [1, 2, 3] b = a b[0] = 4 print(a) --> [4, 2, 3] print(b) --> [4, 2

Python appendextend 方法區別

docList.append(a)方法:將a這個list作為一個元素加入docList,即向docList中加入了一個list元素。 wordList.extend(a)方法:將a這個list中的

1.Python淺複製深複製——copydeepcopy方法

問題:Python裡面如何拷貝一個物件? 參考:《Python Cookbook》 方法說明: copy(x)     Shallow copy operation on arbitrary Python objects.     See the module's __doc__ string

pythonsetfrozenset方法區別

先簡單說一下set t.add('x') # 新增一項 s.update([10,37,42]) # 在s中新增多項 t.remove('H') # 刪除一項 len(s) # set 的長度 x in s # 測試 x 是否是 s

Pythonsort()sorted()的區別

-s 可變對象 傳遞 內置函數 ict pan 16px nbsp lin 1、sort()是可變對象(字典)的方法,無參數,無返回值, sort()會改變可變對象,因此無需返回值。例如: list: 1 >>> a = [4,3,7,8] 2 >

pythonreturnprint的區別

bsp 不同 urn text 一是 什麽 cnblogs style c99 之前遇到這個問題,就試著對比幾種不同的結果,總結啦一下return和print的區別。 總結: return的作用之一是返回計算的值print的作用是輸出數據到控制端在第一個結果中什麽都沒

Python IteratorIterable的區別

pytho 數據 執行 next 判斷 -h 實現 col http (轉載:http://blog.csdn.net/passionkk/article/details/49929887) Python中 list,truple,str,dict這些都可以被叠代,但他們並

python%r%s的區別

ould mat mark post inf info repr pan formatter %r用rper()方法處理對象 %s用str()方法處理對象 有些情況下,兩者處理的結果是一樣的,比如說處理int型對象。 例一: print "I am %d years o

python ifelif的區別

pre else bsp 判斷 nbsp 優秀 自動 滿足 lse 如果程序中判斷事件很多,全部用if的話,會遍歷整個程序,用elif 程序運行時,只要if或後續某一個elif之一滿足邏輯值為True,則程序執行完對應輸出語句後自動結束該輪if-elif(即不會再去冗余地執

python的 == is 的區別

-s 對比 不能 -- 其他 ont clas 通過 數字 == 比較的是兩邊的值 is 比較的是兩邊的內存地址 通過 id()獲取內存地址 小數據池:我們使用過的值存儲在小數據池中,供其他數據使用.   小數據池僅限於 數字 和 字符串:     數字的小數池範圍 -

pythonsysos的區別

move ali 去掉 操作系統 格式 自動 form 出現 class <os和sys的官方解釋> ?os os: This module provides a portable way of using operating system depe

Python*args**kwargs的區別

注:《python核心程式設計第2版》的11.6可變長度的引數—章節中有詳細介紹。 python中我們經常見到,有些函式的引數中有*args和kwargs,那這兩個引數是什麼意思呢? 沒錯,*args和kwargs就是python中的可變引數。 可能很多人用了幾年的 Python 都沒真

Pythonsendsendall的區別

官方文件對socket模式下的socket.send() 和 socket.sendall()解釋如下: sock.sendall(string[, flags])         Send data to the socket. The socket mu

關於pythonlociloc方法

pandas以類似字典的方式來獲取某一列的值 import pandas as pd import numpy as np table = pd.DataFrame(np.zeros((4,2)), index=['a','b','c','d'], columns=['left', 'right'])

pythondefaultdictdict的區別與使用

預設值可以很方便 眾所周知,在Python中如果訪問字典中不存在的鍵,會引發KeyError異常(JavaScript中如果物件中不存在某個屬性,則返回undefined)。但是有時候,字典中的每個鍵都存在預設值是非常方便的。例如下面的例子: strings =