日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

Python的浅拷贝和深拷贝

發(fā)布時間:2025/3/20 python 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python的浅拷贝和深拷贝 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

相對于其他傳統(tǒng)編程語言,Python有一個比較奇怪的特性,即在復(fù)制對象時,有淺拷貝(shallow copy)和深拷貝(deep copy)兩種方式。

淺拷貝和深拷貝只和復(fù)合對象相關(guān)。復(fù)合對象指的是包含對象的對象,如列表(list)、類實例(class instance)等。簡單類型的對象(int、float、string等)不存在淺拷貝和深拷貝的說法。

看下面的實例:

colours1 = ["red", "blue"] colours2 = colours1 print(colours1) print(colours2) print(id(colours1), id(colours2))

輸出結(jié)果:

['red', 'blue'] ['red', 'blue'] 563841065096 563841065096

在上面的例子中,列表colours1被賦值給colours2。Colours1這樣的列表一般被稱為淺列表或普通列表,因為它只包含一些簡單數(shù)據(jù)類型,不包含嵌套結(jié)構(gòu),即不是嵌套列表。id()函數(shù)的值相同表明colours2和colours1這2個列表指向同一個對象,說明colours1被賦值給colours2時,并沒有分配新的內(nèi)存地址,而是將colours2指向了colours1的內(nèi)存地址。下圖給出了相關(guān)的數(shù)據(jù)結(jié)構(gòu)說明。

現(xiàn)在我們看看分配一個新的列表對象給colours2,會發(fā)生什么?

colours2 = ["rouge", "vert"] print(colours1) print(colours2) print(id(colours1), id(colours2))

輸出結(jié)果:

['red', 'blue'] ['rouge', 'vert'] 357368848712 357368848456

跟我們期望的一樣,colours1的值保持不變,一個新的內(nèi)存地址被分配給了colours2。
下面我們再看看colours2不是重新分配對象,而是改變其中一個元素的值,結(jié)果會有什么變化?

colours1 = ["red", "blue"] colours2 = colours1 print(id(colours1), id(colours2)) colours2[1] = "green" print(id(colours1), id(colours2)) print(colours1) print(colours2)

輸出結(jié)果:

1026592727752 1026592727752 1026592727752 1026592727752 ['red', 'green'] ['red', 'green']

可以看到,當(dāng)我們將colours2中的第二個元素重新賦值時,colours1中的值也被自動改變了,很多初學(xué)者在這里都非常迷惑。事實上我們并沒有分配一個新的對象給colours2。colours1和colours2仍然指向同一個列表對象。即我們沒有兩個列表,仍然只有1個,只不過有2個名字。

那對于簡單列表,有沒有完全拷貝的方案呢,有!那就是使用切片方法。因為切片方法是重新生成了一個新對象。

list1 = ['a', 'b', 'c', 'd'] list2 = list1[:] list2[1] = 'x' print(list2) print(list1)

輸出結(jié)果:

['a', 'x', 'c', 'd'] ['a', 'b', 'c', 'd']

但是,如果是像下面這樣的嵌套列表,就又會遇到新的困難和問題。因為切片操作本質(zhì)上仍然是淺拷貝。當(dāng)遇到嵌套列表時,切片方法只復(fù)制子列表的地址,而不是其全部內(nèi)容。

lst1 = ['a', 'b', ['ab', 'ba']] lst2 = lst1[:]

下面的圖給出了lst1和lst2的數(shù)據(jù)結(jié)構(gòu)描述,lst2雖然是一個新建對象,但其中的子列表[‘a(chǎn)b’,’ba’]與lst1中的指的是同一個對象。

如果對lst1和lst2中的第一個元素或第二個元素進(jìn)行賦值,并沒有什么副作用(side effect)

lst1 = ['a', 'b', ['ab', 'ba']] lst2 = lst1[:] lst2[0] = 'c' print(lst1) print(lst2)

輸出結(jié)果:

['a', 'b', ['ab', 'ba']] ['c', 'b', ['ab', 'ba']]


但是,如果改變的是嵌套子列表中的值,那么情況就發(fā)生了變化。

lst2[2][1] = 'd' print(lst1) print(lst2)

輸出結(jié)果:

['a', 'b', ['ab', 'd']] ['c', 'b', ['ab', 'd']]

下面的圖給出了為什么lst1中的嵌套子列表會跟隨lst2發(fā)生變化的原因,因為lst1和lst2的嵌套子列表指向同一個對象。

一個解決方案是使用標(biāo)準(zhǔn)庫的copy模塊。如果我們需要讓一個對象發(fā)生改變時不對原對象產(chǎn)生副作用,就需要一份這個對象的深度拷貝。深拷貝不僅僅拷貝了原始對象自身,也對其包含的值進(jìn)行拷貝,它會遞歸的查找對象中包含的其他對象的引用,來完成更深層次拷貝。因此,深拷貝產(chǎn)生的副本可以隨意修改而不需要擔(dān)心會引起源對象的改變。

對先前的例子使用深拷貝:

from copy import deepcopy lst1 = ['a', 'b', ['ab', 'ba']] lst2 = deepcopy(lst1) print(lst1) print(lst2) print(id(lst1)) print(id(lst2)) print(id(lst1[0])) print(id(lst2[0])) print(id(lst1[2])) print(id(lst2[2]))

輸出結(jié)果:

['a', 'b', ['ab', 'ba']] ['a', 'b', ['ab', 'ba']] 537508176136 537508176200 537506121184 537506121184 537508176712 537508176776

可以看出lst1和lst2的嵌套子列表的內(nèi)存地址不一樣了。下面的圖給出了deepcopy后的數(shù)據(jù)結(jié)構(gòu)情形:

看看改變lst2中相關(guān)值的結(jié)果:

lst2[2][1] = "d" lst2[0] = "c" print(lst1) print(lst2)

輸出結(jié)果:

['a', 'b', ['ab', 'ba']] ['c', 'b', ['ab', 'd']]

上面的代碼最后將數(shù)據(jù)結(jié)構(gòu)變成了這樣:

至于Python中為什么要有淺拷貝和深拷貝的區(qū)別,主要是出于效率方面的考慮。

參考文獻(xiàn):http://www.python-course.eu/python3_deep_copy.php

總結(jié)

以上是生活随笔為你收集整理的Python的浅拷贝和深拷贝的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。