2013-09-28 53 views
9

official Python docs表示使用切片運算符並在Python中進行賦值可以創建切片列表的淺表副本。切片操作是否提供深度或淺度副本?

但是,當我寫的示例代碼:

o = [1, 2, 4, 5] 
p = o[:] 

當我寫:

id(o) 
id(p) 

我得到不同的ID,並附加一個一個列表中的其他列表中不反映。是不是創建了一個深層副本,還是在那裏我錯了?

回答

14

您正在創建一個副本,因爲嵌套值不會被複制,只會被引用。 A 副本也會創建列表引用的值的副本。

演示:

>>> lst = [{}] 
>>> lst_copy = lst[:] 
>>> lst_copy[0]['foo'] = 'bar' 
>>> lst_copy.append(42) 
>>> lst 
[{'foo': 'bar'}] 
>>> id(lst) == id(lst_copy) 
False 
>>> id(lst[0]) == id(lst_copy[0]) 
True 

這裏嵌套字典不被複制;它只是被兩個列表引用。新元素42未共享。

請記住,Python中的所有內容都是一個對象,名稱和列表元素僅僅是對這些對象的引用。列表副本創建一個新的外部列表,但新列表僅僅接收對完全相同的對象的引用。

一個適當的深層副本創建和包含在列表中的所有對象,遞歸的每一個新副本:

>>> from copy import deepcopy 
>>> lst_deepcopy = deepcopy(lst) 
>>> id(lst_deepcopy[0]) == id(lst[0]) 
False 
+0

那麼爲什麼我得到不同的ID – user2528042

+1

@ user2528042:因爲*外部列表對象*是不一樣的。 –

+0

@ user2528042因爲原始列表*被複制到一個新的對象。只是內部的所有元素都不會被複制,所以如果列表包含可變對象(整數不可變),更改該對象將在原始列表和複製列表中更改它,因爲它們都具有對同一對象的引用副本。 – poke

5

你應該知道,使用isid測試可能會產生誤導的真實副本是否正在取得與不可變的和包含不可變的字符串,整數和元組等對象。

考慮一個容易理解的例子實習字符串:

>>> l1=['one'] 
>>> l2=['one'] 
>>> l1 is l2 
False 
>>> l1[0] is l2[0] 
True 

現在做的l1淺拷貝和測試不可改變的字符串:

>>> l3=l1[:] 
>>> l3 is l1 
False 
>>> l3[0] is l1[0] 
True 

現在讓通過l1[0]包含字符串的副本:

>>> s1=l1[0][:] 
>>> s1 
'one' 
>>> s1 is l1[0] is l2[0] is l3[0] 
True    # they are all the same object 

T RY一個deepcopy的,每一個元素應該被複制:

>>> from copy import deepcopy 
>>> l4=deepcopy(l1) 
>>> l4[0] is l1[0] 
True 

在每種情況下,字符串'one'被拘留到Python的內部不可改變的字符串和is的緩存將表明他們是相同的(它們具有相同的id) 。它的實現和版本取決於實現的內容以及實現的時間,所以你不能依賴它。它可以是一個重要的記憶和性能增強。

您可以強制不立刻被拘禁的例子:

>>> s2=''.join(c for c in 'one') 
>>> s2==l1[0] 
True 
>>> s2 is l1[0] 
False 

然後你就可以使用Python intern function,使該字符串來表示緩存的對象,如果發現:

>>> l1[0] is s2 
False 
>>> s2=intern(s2) 
>>> l1[0] is s2 
True 

同樣適用於不可變元組:

>>> t1=('one','two') 
>>> t2=t1[:] 
>>> t1 is t2 
True 
>>> t3=deepcopy(t1) 
>>> t3 is t2 is t1 
True 

immutables(如整數)的d可變列表可以讓列表成員埋葬:

>>> li1=[1,2,3] 
>>> li2=deepcopy(li1) 
>>> li2 == li1 
True 
>>> li2 is li1 
False 
>>> li1[0] is li2[0] 
True 

所以,你可以使用你知道將複製的東西,但最終的結果是另一個引用的實習不可變對象蟒蛇行動。如果項目是可變的,is測試只是一個副本的決定性測試。

+0

在Python 3上,'intern'已經被移動到'sys'模塊,所以你需要執行'import sys; S = sys.intern(S)' – dawg

相關問題