Python 中的賦值語句不會(huì)創(chuàng)建對(duì)象的拷貝,,僅僅只是將名稱綁定至一個(gè)對(duì)象,。對(duì)于不可變對(duì)象,通常沒什么差別,,但是處理可變對(duì)象或可變對(duì)象的集合時(shí),,你可能需要?jiǎng)?chuàng)建這些對(duì)象的 “真實(shí)拷貝”,也就是在修改創(chuàng)建的拷貝時(shí)不改變?cè)嫉膶?duì)象,。 本文將以圖文方式介紹 Python 中復(fù)制或“克隆”對(duì)象的操作,。 首先介紹一下 Python 中淺拷貝與深拷貝的區(qū)別: 淺拷貝:淺拷貝意味著構(gòu)造一個(gè)新的集合對(duì)象,然后用原始對(duì)象中找到的子對(duì)象的引用來填充它,。從本質(zhì)上講,,淺層的復(fù)制只有一層的深度。復(fù)制過程不會(huì)遞歸,,因此不會(huì)創(chuàng)建子對(duì)象本身的副本,。 深拷貝:深拷貝使復(fù)制過程遞歸。這意味著首先構(gòu)造一個(gè)新的集合對(duì)象,,然后遞歸地用在原始對(duì)象中找到的子對(duì)象的副本填充它,。以這種方式復(fù)制一個(gè)對(duì)象,遍歷整個(gè)對(duì)象樹,,以創(chuàng)建原始對(duì)象及其所有子對(duì)象的完全獨(dú)立的克隆,。 賦值與引用 在開始淺拷貝與深拷貝前,我們先來看一下 Python 中的賦值與引用,。 從字面上看,,上述語句創(chuàng)建了變量和,并且和的賦值都為一個(gè)列表,。但是,,Python 的賦值語句并不會(huì)復(fù)制對(duì)象,,而是會(huì)重新創(chuàng)建一個(gè)對(duì)象的引用。 可以看出,,和都引用了同一個(gè)列表,。 創(chuàng)建淺拷貝 不少教程里都會(huì)提到,如果你有一個(gè)列表,,當(dāng)你想要修改列表中的值但卻不想影響原始對(duì)象時(shí),,可以使用 復(fù)制(淺拷貝)一個(gè)列表。 我們先來試一下: 沒錯(cuò),,和分別指向了不同的列表,。當(dāng)修改列表中的值時(shí),并不會(huì)對(duì)對(duì)象產(chǎn)生影響,。 之所以說 語句是淺拷貝,,是因?yàn)檫@種修改只對(duì)一層對(duì)象有效,當(dāng)列表中有子對(duì)象時(shí),,對(duì)子對(duì)象的修改將影響原始對(duì)象和淺拷貝對(duì)象,。 為了解釋這一說法,讓我們先創(chuàng)建一個(gè)嵌套列表,,并使用 函數(shù)創(chuàng)建淺拷貝。 這里是有著和一樣內(nèi)容的新的獨(dú)立的對(duì)象,。 可以看到 和 分別指向了不同的對(duì)象,。 對(duì)第一層 的修改,,將不會(huì)對(duì) 副本造成影響。 但是,,因?yàn)槲覀冎粍?chuàng)建了原始列表的一個(gè)淺拷貝,,所以 仍然包含對(duì) 中存儲(chǔ)的原始子對(duì)象的引用。 也就是如上圖所示,, 和 的子列表都指向了相同的對(duì)象,。 子對(duì)象沒有被復(fù)制,它們只是在復(fù)制的列表中被再次引用,。 因此,,當(dāng)你修改中的一個(gè)子對(duì)象時(shí),這種修改也會(huì)反映到中—— 這是因?yàn)閮蓚€(gè)列表共享相同的子對(duì)象,。這種復(fù)制只是一個(gè)淺的,,一個(gè)層級(jí)的復(fù)制: 如果我們?cè)诘谝徊街袆?chuàng)建了一個(gè) 的深拷貝,那么兩個(gè)對(duì)象就完全獨(dú)立了。這是對(duì)象的淺拷貝和深拷貝之間的實(shí)際區(qū)別,。 使用 Python 標(biāo)準(zhǔn)庫中的 模塊可以創(chuàng)建深拷貝,,這個(gè)模塊為創(chuàng)建任意 Python 對(duì)象的淺拷貝和深拷貝提供了一個(gè)簡單的接口。 創(chuàng)建深拷貝 這次我們使用 函數(shù)創(chuàng)建一個(gè)對(duì)象的深拷貝: 從圖中可以看出 和 中的子對(duì)象指向了不同的對(duì)象,,如果對(duì) 的子對(duì)象進(jìn)行修改,,將不會(huì)影響 。 這一次,,原始對(duì)象和復(fù)制對(duì)象都是完全獨(dú)立的,。如前面所說,,遞歸克隆了 ,包括它的所有子對(duì)象: 模塊中的函數(shù)也可以創(chuàng)建對(duì)象的淺拷貝,。使用可以明確地表示創(chuàng)建淺拷貝,。對(duì)于內(nèi)置集合,,簡單地使用、和等工廠函數(shù)來創(chuàng)建淺拷貝是更加 Pythonic 的,。 復(fù)制任意 Python 對(duì)象 和 函數(shù)可用于復(fù)制任意對(duì)象,。以前面的列表復(fù)制示例為基礎(chǔ)。讓我們從定義一個(gè)簡單的 2D 點(diǎn)類開始: 函數(shù)使我們可以輕松地在 Python 解釋器中檢查從這個(gè)類創(chuàng)建的對(duì)象,。 接下來,,我們將創(chuàng)建一個(gè) Point 實(shí)例,然后使用 模塊復(fù)制(淺拷貝)它: 和 分別指向了不同的 Point 實(shí)例,。因?yàn)槲覀兊?Point 對(duì)象使用不可變類型(int)作為其坐標(biāo),,所以在這種情況下,淺拷貝和深拷貝沒有區(qū)別,。但我馬上會(huì)展開這個(gè)例子,。 接下來定義另一個(gè)類來表示 2D 矩形。矩形將使用 Point 對(duì)象來表示它們的坐標(biāo): 跟前面 list 的例子一樣,,和的子對(duì)象都有相同的引用,。在對(duì)象層級(jí)中修改一個(gè)對(duì)象,,將看到這個(gè)變化也反映在淺拷貝的副本中: 接下來創(chuàng)建 Rectangle 的深拷貝并對(duì)其進(jìn)行修改: 可以看出,深拷貝完全獨(dú)立于原始對(duì)象和淺拷貝對(duì)象,。 參閱 copy 模塊文檔 可以對(duì)復(fù)制進(jìn)行進(jìn)一步的研究,。例如,對(duì)象可以通過定義特殊的方法 和 來控制如何復(fù)制它們,。 謹(jǐn)記三件事 創(chuàng)建對(duì)象的淺拷貝不會(huì)克隆子對(duì)象,。因此,拷貝不會(huì)完全獨(dú)立于原始對(duì)象,。 一個(gè)對(duì)象的深拷貝會(huì)遞歸地克隆子對(duì)象,。克隆對(duì)象完全獨(dú)立于原始對(duì)象,,但是創(chuàng)建深拷貝速度較慢,。 可以使用 模塊復(fù)制任意對(duì)象(包括自定義類)。 部分內(nèi)容翻譯自: https:///copying-python-objects/ 本文博客地址: (完) 看完本文有收獲,?請(qǐng)轉(zhuǎn)發(fā)分享給更多人 關(guān)注「Python那些事」,,做全棧開發(fā)工程師 |
|