Illustrations by Leon Tukker 作者:PayneLi,Python全家桶,主要講述數(shù)據(jù)挖掘、機器學習和深度學習領(lǐng)域的前沿技術(shù),同時還會推薦一些行業(yè)最新論文、技術(shù)專家的經(jīng)驗分享。 在平時工作中,經(jīng)常涉及到數(shù)據(jù)的傳遞,在數(shù)據(jù)傳遞使用過程中,可能會發(fā)生數(shù)據(jù)被修改的問題。為了防止數(shù)據(jù)被修改,就需要在傳遞一個副本,即使副本被修改,也不會影響原數(shù)據(jù)的使用。為了生成這個副本,就產(chǎn)生了拷貝。今天就說一下Python中的深拷貝與淺拷貝的問題。 概念普及:對象、可變類型、引用 數(shù)據(jù)拷貝會涉及到Python中對象、可變類型、引用這3個概念,先來看看這幾個概念,只有明白了他們才能更好的理解深拷貝與淺拷貝到底是怎么一回事。 在Python中,對對象有一種很通俗的說法,萬物皆對象。說的就是構(gòu)造的任何數(shù)據(jù)類型都是一個對象,無論是數(shù)字,字符串,還是函數(shù),甚至是模塊,Python都對當做對象處理。 所有Python對象都擁有三個屬性:身份、類型、值。 看一個簡單的例子: In [1]: name = 'laowang' # name對象 在Python中,按更新對象的方式,可以將對象分為2大類:可變對象與不可變對象。
在 Python 程序中,每個對象都會在內(nèi)存中申請開辟一塊空間來保存該對象,該對象在內(nèi)存中所在位置的地址被稱為引用。在開發(fā)程序時,所定義的變量名實際就對象的地址引用。 引用實際就是內(nèi)存中的一個數(shù)字地址編號,在使用對象時,只要知道這個對象的地址,就可以操作這個對象,但是因為這個數(shù)字地址不方便在開發(fā)時使用和記憶,所以使用變量名的形式來代替對象的數(shù)字地址。 在 Python 中,變量就是地址的一種表示形式,并不開辟開辟存儲空間。 就像 IP 地址,在訪問網(wǎng)站時,實際都是通過 IP 地址來確定主機,而 IP 地址不方便記憶,所以使用域名來代替 IP 地址,在使用域名訪問網(wǎng)站時,域名被解析成 IP 地址來使用。 通過一個例子來說明變量和變量指向的引用就是一個東西 In [11]: age = 18 逐步深入:引用賦值 上邊已經(jīng)明白,引用就是對象在內(nèi)存中的數(shù)字地址編號,變量就是方便對引用的表示而出現(xiàn)的,變量指向的就是此引用。賦值的本質(zhì)就是讓多個變量同時引用同一個對象的地址。 那么在對數(shù)據(jù)修改時會發(fā)生什么問題呢?
下面通過案例來理解一下: a與b在內(nèi)存中都是指向1的引用,所以a、b的引用是相同的
現(xiàn)在再給a重新賦值,看看會發(fā)生什么變化? 從下面不難看出:當給a 賦新的對象時,將指向現(xiàn)在的引用,不在指向舊的對象引用。 In [1]: a = 1
原理圖如下: 仍然通過一個實例來體會一下,可變對象引用賦值的過程。 當改變l1時,整個列表的引用會指新的對象,但是l1與l2都是指向保存的同一個列表的引用,所以引用地址不會變。
主旨詳解:淺拷貝、深拷貝 經(jīng)過前2部分的解讀,大家對對象的引用賦值應(yīng)該有了一個清晰的認識了。 下面大家思考一個這樣的問題:Python中如何解決原始數(shù)據(jù)在函數(shù)傳遞之后不受影響了? 這個問題Python已經(jīng)幫我們解決了,使用對象的拷貝或者深拷貝就可以愉快的解決了。 下面具體來看看Python中的淺拷貝與深拷貝是如何實現(xiàn)的。
不可變對象只在修改的時候才會在內(nèi)存中開辟新的空間, 而拷貝實際上是讓多個對象同時指向一個引用,和對象的賦值沒區(qū)別。 同樣的,通過一個實例來感受一下:不難看出,a與b指向相同的引用,不可變對象的拷貝就是對象賦值。 In [11]: import copy 對于不可變對象的拷貝,對象的引用并沒有發(fā)生變化,那么可變對象的拷貝會不會和不可變對象一樣了?我們接著往下看。 通過下面這個實例可以看出:可變對象的拷貝,會在內(nèi)存中開辟一個新的空間來保存拷貝的數(shù)據(jù)。當再改變之前的對象時,對拷貝之后的對象沒有任何影響。
原理圖如下: 現(xiàn)在再回到剛才那個問題,是不是淺拷貝就可以解決原始數(shù)據(jù)在函數(shù)傳遞之后不變的問題了?下面看一個稍微復(fù)雜一點的數(shù)據(jù)結(jié)構(gòu)。 通過下面這個實例可以發(fā)現(xiàn):復(fù)雜對象在拷貝時,并沒有解決數(shù)據(jù)在傳遞之后,數(shù)據(jù)改變的問題。 出現(xiàn)這種原因,是copy() 函數(shù)在拷貝對象時,只是將指定對象中的所有引用拷貝了一份,如果這些引用當中包含了一個可變對象的話,那么數(shù)據(jù)還是會被改變。 這種拷貝方式,稱為淺拷貝。 In [35]: a = [1, 2] 原理圖如下: 對于上邊這種狀況,Python還提供了另一種拷貝方式(深拷貝)來解決。
接下來我們看看,要是將上邊的拷貝實例用使用深拷貝的話,原始數(shù)據(jù)改變的問題還會不會存在了? 下面的實例清楚的告訴我們:之前的問題就可以完美解決了。
原理圖如下: 查漏補缺 為什么Python默認的拷貝方式是淺拷貝?
本文知識點總結(jié):
|
|