寫python**時,淺拷貝和深拷貝是經常遇見的問題,今天看書時碰到,結合書上的例子,再次整理一遍。例子來自《流暢的python》
>>
> l1 =[3
,[55,
44],(
7,8,
9)]>>
> l2 =
list
(l1)
#使用list型別內建的型別構造方法來複製列表l1
>>
> l2[3
,[55,
44],(
7,8,
9)]>>
> l2 == l1
true
>>
> l2 is l1
false
首先明確,在python中,賦值操作的原理是先建立物件(python一切皆物件),然後將左值作為右值的引用繫結在一起。
上面的**使用list內建的構造方法完成了對列表l1的複製,得到了副本l2。l1和l2的值是相等的(使用』=='運算子來判斷得到),但是l2和l1是兩個不同的物件(有is運算子得到)。除了類內建的構造方法,語句l2 = l1[:]也可以建立副本l2。
!!!這裡構造方法或[:]做的都是淺複製(即複製了最外層容器,但是副本中的元素是原容器中元素的引用)。如果所有元素都是不可變的(例如原子不可變資料型別:str,bytes和數值型別),那麼這樣沒有問題,還能節省記憶體。但是,對於可變資料型別(如列表等),這樣的引用就很容易出現問題。
l1 =[3
,[66,
55,44]
,(7,
8,9)
]l2 =
list
(l1)
# l2是l1的淺複製副本;
100)
# 最外層容器是不同的物件,對l2沒有影響;
l1[1
],remove(55)
# l2[1]是l1[1]的引用,本質是同乙個物件;
print
('l1'
, l1)
print
('l2'
, l2)
l2[1]+=
[33,22
]# 對可變的物件來說,如l2[1]引用的列表,+=運算子就地修改列表。這次修改在l1[1]中也有體現,因為它是l2[1]的別名。
l2[2]+=
(10,11
)# 對元組來說,+=運算子建立乙個新元組,然後重新繫結給變數l2[2]。這等同於l2[2] = l2[2] + (10, 11)。現在,l1和l2中最後位置上的元組不是同乙個物件。
print
('l1'
, l1)
print
('l2'
, l2)
列印結果:
l1:[3,
[66,44
],(7
,8,9
),100]
l2:[3,
[66,44
],(7
,8,9
)]l1:[3,
[66,44
,33,22
],(7
,8,9
),100]
l2:[3,
[66,44
,33,22
],(7
,8,9
,10,11
)]
copy模組中提供的deepcopy和copy函式能為任意物件做深拷貝和淺拷貝。
注意,一般來說,深複製不是件簡單的事。如果物件有迴圈引用,那麼這個樸素的演算法會進入無限迴圈。deepcopy函式會記住已經複製的物件,因此能優雅地處理迴圈引用,如下面所示:b引用a,然後追加到a中;deepcopy會想辦法複製a。
>>
> a =[10
,20]>>
> b =
[a,30
]>>
>>
> a[10
,20,[
[...
],30]
]>>
>
from copy import deepcopy
>>
> c = deepcopy(a)
>>
> c[10
,20,[
[...
],30]
]
深複製有時可能太深了。例如,物件可能會引用不該複製的外部資源或單例值。此時可以通過實現特殊方法__copy__()和__deepcopy__(),控制copy和deepcopy的行為。 python中的淺拷貝和深拷貝
不得不說 python核心程式設計 是一本好書,看到其中一節做一下隨筆。在python中,當建立乙個物件後,然後把它賦給另乙個物件時,python並沒有去拷貝這個物件,而是拷貝了這個物件的引用。看不懂沒關係,我們看乙個例子。raw list first second 12 32 copy list ...
python中的深拷貝和淺拷貝
淺拷貝 copy 不拷貝物件的內容,僅僅拷貝子物件的引用 深拷貝 deepcopy 會連同拷貝子物件的記憶體,對子物件的修改不會影響源物件 下面用 來測試 import copy deftest copy 淺拷貝測試 a 10 20,5,6 b copy.copy a print a a,end t...
python中的淺拷貝和深拷貝
本篇介紹下python中的深拷貝和淺拷貝,主要從基本型別 類 不可變型別等方面進行介紹。1.介紹拷貝之前首先應該明白is和 的區別,即is表示同乙個物件,比較的是值 a 1000 b 1000 a b true a is bfalse class person object def init sel...