[Python][6]中的拷貝概念與[C++][6]中的一樣。也即深拷貝就是對對象資源的拷貝,淺拷貝就是對引用的拷貝。這與我們直覺中的拷貝有點不一樣,所以在實際應用中容易搞混。
在python中,變量名不用事先聲明,變量類型也不用事先聲明,變量會在第一次賦值時自動聲明,在創建時,也就是賦值的時候,解釋器會根據語法和右側的操作數來決定新對象的類型。
要保持追蹤內存中的對象,Python使用引用計數這一簡單技術。也就是說python內部記錄著所有使用中的對象各有多少引用。當對象被創建時,就創建一個引用計數,并且被設置為1(事實上它并不是1,可能是python本身對創建的對象有引用)。
>>> x = 123 #新創建的整型對象123賦值給x,其引用計數為1>>> y = x #y是x的別名,現在整型對象123的引用計數為2先看代碼:
>>> a = [1,2,3]>>> b = a>>> b.append(111)>>> PRint(a,b)>>> [1,2,3,111] [1,2,3,111]>>>print(id(a),id(b))>>> 64880112 64880112從上面可見,對象的賦值實際上是對象的引用。當創建一個對象,然后把它賦給另一個變量的時候,python并沒有拷貝這個對象,而只是拷貝了這個對象的引用。
如果你想修改一個對象,而且想讓原始的對象不受影響,那你就需要對象復制??梢允褂萌缦聨讉€方法:
(1)、使用切片[:]操作進行拷貝 (2)、使用工廠函數(如list/dir/set)等進行拷貝 (3)、copy.copy()(需導入copy模塊)
>>> person = ["name",["save",100]]>>> tom = person[:]>>> jack = list(person)>>> tom['name', ['save', 100]]>>> jack['name', ['save', 100]]>>> [id(x) for x in (person,tom,jack)][52590472, 63910176, 67064136]在上面的代碼中,我們采用切片和工廠函數list進行拷貝,可以看到拷貝后的tom,jack,person的id值均不同,那這是否是已經達到我們想要的拷貝呢?
現在我們對拷貝的tom,jack,進行一些操作:
>>> tom[0] = "tom">>> jack[0] = "jack">>> tom['tom', ['save', 100]]>>> jack['jack', ['save', 100]]>>> tom[1][1] = 50>>> tom['tom', ['save', 50]]>>> jack['jack', ['save', 50]]上面的實例中,我們成功修改 了姓名,但是對存款的修改卻沒有達到預想的效果(前提是我們希望復制后的tom,jack 互不影響)。 原因是我們只做了淺拷貝。對一個對象進行淺拷貝其實是新創建了一個類型跟原對象一樣,其內容是原對象元素的引用,換句話說,這個拷貝對象本身是新的,但是它的內容不是。 那么為什么修改姓名時沒有互相影響,而修改存款時會互相影響?這是因為在這兩個列表對象中,第一個對象是不可變對象(是個字符串類型),第二個對象是可變對象(一個列表)。 在python中字符串不可以修改,所以在為tom和jack重新命名的時候,會重新創建一個’tom’和“jack”對象,替換舊的’name’對象。這就說明了,淺復制(shallow copy),它復制了對象,但對于對象中的元素,依然使用引用.
>>> import copy>>> aa = [1,2,3]>>> bb =copy.copy(aa)>>> id(aa)63911536>>> id(bb)67064176>>> bb[0] = 100>>> aa[1, 2, 3]>>> bb #由于數字不可變,修改的時候會替換舊的對象[100, 2, 3]下面試試復制的對象中包含可變對象:
>>> lis = [[1],["aaa"]]>>> clis = copy.copy(lis)>>> lis[[1], ['aaa']]>>> clis[[1], ['aaa']]>>> clis[0].append("bbbb")>>> lis[[1, 'bbbb'], ['aaa']]>>> clis[[1, 'bbbb'], ['aaa']]上面的示例中,復制后的clis的修改影響到了原來的lis,這并不是我們想要的。如果希望復制一個容器對象,以及它里面的所有元素(包含元素的子元素),使用copy.deepcopy,這個方法會消耗一些時間和空間,不過,如果你需要完全復制,這是唯一的方法.
#深拷貝>>> deeplis = copy.deepcopy(lis)>>> deeplis[[1, 'bbbb'], ['aaa']]>>> deeplis[0].append("aaaa")>>> deeplis[[1, 'bbbb', 'aaaa'], ['aaa']]>>> lis[[1, 'bbbb'], ['aaa']]注意:
1、對于非容器類型(如數字、字符串、和其他‘原子’類型的對象)沒有被拷貝一說。
2、如果元祖變量只包含原子類型對象,則不能深copy。
參考:http://www.49028c.com/BeginMan/p/3197649.html
新聞熱點
疑難解答