在寫(xiě)Python過(guò)程中,經(jīng)常會(huì)遇到對(duì)象的拷貝,如果不理解淺拷貝和深拷貝的概念,你的代碼就可能出現(xiàn)一些問(wèn)題。所以,在這里按個(gè)人的理解談?wù)勊鼈冎g的區(qū)別。
成都創(chuàng)新互聯(lián)公司:從2013年創(chuàng)立為各行業(yè)開(kāi)拓出企業(yè)自己的“網(wǎng)站建設(shè)”服務(wù),為成百上千家公司企業(yè)提供了專業(yè)的網(wǎng)站制作、成都網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)和網(wǎng)站推廣服務(wù), 按需網(wǎng)站策劃由設(shè)計(jì)師親自精心設(shè)計(jì),設(shè)計(jì)的效果完全按照客戶的要求,并適當(dāng)?shù)奶岢龊侠淼慕ㄗh,擁有的視覺(jué)效果,策劃師分析客戶的同行競(jìng)爭(zhēng)對(duì)手,根據(jù)客戶的實(shí)際情況給出合理的網(wǎng)站構(gòu)架,制作客戶同行業(yè)具有領(lǐng)先地位的。
一、賦值(assignment)
在《Python FAQ1》一文中,對(duì)賦值已經(jīng)講的很清楚了,關(guān)鍵要理解變量與對(duì)象的關(guān)系。
12345
a = [1, 2, 3] b = a print(id(a), id(b), sep='\n')139701469405552139701469405552
在Python中,用一個(gè)變量給另一個(gè)變量賦值,其實(shí)就是給當(dāng)前內(nèi)存中的對(duì)象增加一個(gè)“標(biāo)簽”而已。
如上例,通過(guò)使用內(nèi)置函數(shù) id() ,可以看出 a 和 b 指向內(nèi)存中同一個(gè)對(duì)象。a is b會(huì)返回 True 。
二、淺拷貝(shallow copy)
注意:淺拷貝和深拷貝的不同僅僅是對(duì)組合對(duì)象來(lái)說(shuō),所謂的組合對(duì)象就是包含了其它對(duì)象的對(duì)象,如列表,類實(shí)例。而對(duì)于數(shù)字、字符串以及其它“原子”類型,沒(méi)有拷貝一說(shuō),產(chǎn)生的都是原對(duì)象的引用。
所謂“淺拷貝”,是指創(chuàng)建一個(gè)新的對(duì)象,其內(nèi)容是原對(duì)象中元素的引用。(拷貝組合對(duì)象,不拷貝子對(duì)象)
常見(jiàn)的淺拷貝有:切片操作、工廠函數(shù)、對(duì)象的copy()方法、copy模塊中的copy函數(shù)。
12345678910
a = [1, 2, 3] b = list(a) print(id(a), id(b)) # a和b身份不同140601785066200 140601784764968 for x, y in zip(a, b): # 但它們包含的子對(duì)象身份相同... print(id(x), id(y))... 140601911441984 140601911441984140601911442016 140601911442016140601911442048 140601911442048
從上面可以明顯的看出來(lái),a 淺拷貝得到 b,a 和 b 指向內(nèi)存中不同的 list 對(duì)象,但它們的元素卻指向相同的 int 對(duì)象。這就是淺拷貝!
三、深拷貝(deep copy)
所謂“深拷貝”,是指創(chuàng)建一個(gè)新的對(duì)象,然后遞歸的拷貝原對(duì)象所包含的子對(duì)象。深拷貝出來(lái)的對(duì)象與原對(duì)象沒(méi)有任何關(guān)聯(lián)。
深拷貝只有一種方式:copy模塊中的deepcopy函數(shù)。
1234567891011
import copy a = [1, 2, 3] b = copy.deepcopy(a) print(id(a), id(b))140601785065840 140601785066200 for x, y in zip(a, b):... print(id(x), id(y))... 140601911441984 140601911441984140601911442016 140601911442016140601911442048 140601911442048
看了上面的例子,有人可能會(huì)疑惑:
為什么使用了深拷貝,a和b中元素的id還是一樣呢?
答:這是因?yàn)閷?duì)于不可變對(duì)象,當(dāng)需要一個(gè)新的對(duì)象時(shí),python可能會(huì)返回已經(jīng)存在的某個(gè)類型和值都一致的對(duì)象的引用。而且這種機(jī)制并不會(huì)影響 a 和 b 的相互獨(dú)立性,因?yàn)楫?dāng)兩個(gè)元素指向同一個(gè)不可變對(duì)象時(shí),對(duì)其中一個(gè)賦值不會(huì)影響另外一個(gè)。
我們可以用一個(gè)包含可變對(duì)象的列表來(lái)確切地展示“淺拷貝”與“深拷貝”的區(qū)別:
1234567891011121314151617181920
import copy a = [[1, 2],[5, 6], [8, 9]] b = copy.copy(a) # 淺拷貝得到b c = copy.deepcopy(a) # 深拷貝得到c print(id(a), id(b)) # a 和 b 不同139832578518984 139832578335520 for x, y in zip(a, b): # a 和 b 的子對(duì)象相同... print(id(x), id(y))... 139832578622816 139832578622816139832578622672 139832578622672139832578623104 139832578623104 print(id(a), id(c)) # a 和 c 不同139832578518984 139832578622456 for x, y in zip(a, c): # a 和 c 的子對(duì)象也不同... print(id(x), id(y))... 139832578622816 139832578621520139832578622672 139832578518912139832578623104 139832578623392
從這個(gè)例子中可以清晰地看出淺拷貝與深拷貝地區(qū)別。
總結(jié):
1、賦值:簡(jiǎn)單地拷貝對(duì)象的引用,兩個(gè)對(duì)象的id相同。
2、淺拷貝:創(chuàng)建一個(gè)新的組合對(duì)象,這個(gè)新對(duì)象與原對(duì)象共享內(nèi)存中的子對(duì)象。
3、深拷貝:創(chuàng)建一個(gè)新的組合對(duì)象,同時(shí)遞歸地拷貝所有子對(duì)象,新的組合對(duì)象與原對(duì)象沒(méi)有任何關(guān)聯(lián)。雖然實(shí)際上會(huì)共享不可變的子對(duì)象,但不影響它們的相互獨(dú)立性。
淺拷貝和深拷貝的不同僅僅是對(duì)組合對(duì)象來(lái)說(shuō),所謂的組合對(duì)象就是包含了其它對(duì)象的對(duì)象,如列表,類實(shí)例。而對(duì)于數(shù)字、字符串以及其它“原子”類型,沒(méi)有拷貝一說(shuō),產(chǎn)生的都是原對(duì)象的引用。
賦值(=),就是創(chuàng)建了對(duì)象的一個(gè)新的引用,修改其中任意一個(gè)變量都會(huì)影響到另一個(gè)。
淺拷貝:創(chuàng)建一個(gè)新的對(duì)象,但它包含的是對(duì)原始對(duì)象中包含項(xiàng)的引用(如果用引用的方式修改其中一個(gè)對(duì)象,另外一個(gè)也會(huì)修改改變){1,完全切片方法;2,工廠函數(shù),如list();3,copy模塊的copy()函數(shù)}
深拷貝:創(chuàng)建一個(gè)新的對(duì)象,并且遞歸的復(fù)制它所包含的對(duì)象(修改其中一個(gè),另外一個(gè)不會(huì)改變){copy模塊的deep.deepcopy()函數(shù)}
在Python中對(duì)象的賦值(=)其實(shí)就是對(duì)象的引用。即:當(dāng)創(chuàng)建一個(gè)對(duì)象,把它賦值給另一個(gè)變量時(shí),python并沒(méi)有拷貝這個(gè)對(duì)象,只是拷貝了這個(gè)對(duì)象的引用而已。
Python中對(duì)象的拷貝分為:淺拷貝(copy)和深拷貝(deepcopy)。
淺拷貝:拷貝了最外圍的對(duì)象本身,內(nèi)部的元素都只是拷貝了一個(gè)引用而已。也就是,將原對(duì)象在內(nèi)存中引用地址拷貝過(guò)來(lái),然后讓新的對(duì)象指向這個(gè)地址。可以使用“=”或列表自帶的copy()函數(shù)(如list.copy()),或使用copy模塊的copy()函數(shù)。
深拷貝:外圍和內(nèi)部元素都進(jìn)行了拷貝對(duì)象本身,而不是引用。即把對(duì)象復(fù)制一遍,并且該對(duì)象中引用的其他對(duì)象也同時(shí)復(fù)制,完全得到一個(gè)新的一模一樣的對(duì)象,對(duì)新對(duì)象里的值進(jìn)行修改不會(huì)影響原有對(duì)象,新對(duì)象和原對(duì)象完全分離開(kāi)。深拷貝只能使用copy模塊中deepcopy()函數(shù),使用前要導(dǎo)入:from copy import deepcopy。
Python中對(duì)象分為不可變對(duì)象 、可變對(duì)象。
不可變對(duì)象:一旦創(chuàng)建就不可修改的對(duì)象,例如:字符串、元組、數(shù)字
可變對(duì)象:可以修改的對(duì)象,例如:列表、字典。
其中Python中的切片可以應(yīng)用于:列表、元組、字符串,但不能應(yīng)用于字典。
而深淺拷貝,可應(yīng)用于序列(列表、元組、字符串),也可應(yīng)用于字典。
其中不可變對(duì)象,不管是深拷貝還是淺拷貝,地址值在拷貝后的值都是一樣的。
以下以元組(不可變類型)為例
從上述示例可以看出:
不可變對(duì)象類型,沒(méi)有被拷貝的說(shuō)法,即便是用深拷貝,查看id的話也是一樣的,如果對(duì)其重新賦值,也只是新創(chuàng)建一個(gè)對(duì)象,替換掉舊的而已。
所以不可變類型,不管是深拷貝還是淺拷貝,地址值和拷貝后的值都是一樣的。
以下以列表(可變類型)為例
第一種方法:使用=號(hào)淺拷貝
輸出結(jié)果:
第二種方法:使用copy淺拷貝
輸出結(jié)果:
第三種方法:使用deepcopy深拷貝
輸出結(jié)果:
從上述示例可以看出:
=淺拷貝:值相等,地址相等
copy淺拷貝:值相等,地址不相等
deepcopy深拷貝:值相等,地址不相等
總結(jié):
1,深淺拷貝都是對(duì)源對(duì)象的復(fù)制,占用不同的內(nèi)存空間。
2,不可變類型的對(duì)象,對(duì)于深淺拷貝毫無(wú)影響,最終的地址值和值都是相等的。
3,可變類型的對(duì)象,使用=淺拷貝時(shí), 值相等,地址相等,對(duì)新對(duì)象里的值進(jìn)行修改同時(shí)會(huì)影響原有對(duì)象;使用copy淺拷貝時(shí)值相等,地址不相等;使用deepcopy深拷貝時(shí)值相等,地址不相等??梢钥闯鲠槍?duì)可變類型copy淺拷貝和deepcopy深拷貝,對(duì)新對(duì)象里的值進(jìn)行修改不會(huì)影響原有對(duì)象。
名稱欄目:深拷貝函數(shù)python 深淺拷貝實(shí)現(xiàn)方法
當(dāng)前路徑:http://aaarwkj.com/article36/dooospg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供商城網(wǎng)站、企業(yè)網(wǎng)站制作、微信公眾號(hào)、品牌網(wǎng)站制作、網(wǎng)站收錄、網(wǎng)站內(nèi)鏈
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)