內存的淺拷貝和深拷貝是面試時常常被問到的問題,若是不能理解其本質原理,有可能會答非所問,給面試官留下很差的印象。另外,理解淺拷貝和深拷貝的原理,還能夠幫助咱們理解Python內存機制。這篇文章將會經過一些例子,來驗證內存拷貝的過程,幫助你們理解內存拷貝的原理。python
咱們首先得知道Python3中的數據被分爲可變類型和不可變類型面試
對於可變類型和不可變類型,它們在淺拷貝和深拷貝中的表現是不同的,下面咱們就經過具體的例子來引出對應的結論。數據結構
咱們先來貼一個例子,而後你們能夠先思考下結果會是怎樣的。app
def shadow_copy_test(): """ 對淺copy進行驗證 :return: """ # 不可變數據類型 param_a = 17 param_b = "paramB" param_c = (18, "paramC") copy_param_a = copy.copy(param_a) copy_param_b = copy.copy(param_b) copy_param_c = copy.copy(param_c) print("驗證不可變數據類型") print(id(param_a)) print(id(copy_param_a)) print(id(param_b)) print(id(copy_param_b)) print(id(param_c)) print(id(copy_param_c)) print("======================") # 可變數據類型 param_d = [[2, 3], 18, "paramD"] param_e = {"key1": 18, "key2": "paramE", "key3": [1, 2]} param_f = {18, "paramF"} copy_param_d = copy.copy(param_d) copy_param_e = copy.copy(param_e) copy_param_f = copy.copy(param_f) print("驗證可變數據類型") print(id(param_d)) print(id(copy_param_d)) print(id(param_e)) print(id(copy_param_e)) print(id(param_f)) print(id(copy_param_f)) # 運行結果 驗證不可變數據類型 4455468864 4455468864 4457955120 4457955120 4457945040 4457945040 ====================== 驗證可變數據類型 4458366368 4458367168 4457911312 4457911552 4457982144 4458284768
由此咱們能夠看出,對於不可變類型,淺拷貝並不會更改內存地址,而對於可變數據類型,會產生一個新的內存地址。接下來咱們再來看看對於可變數據類型,去修改其中的元素會怎麼樣:spa
print("驗證列表中元素") # 驗證列表中第一個元素是否相等 print(id(param_d[1])) print(id(copy_param_d[1])) print(id(param_d[0])) print(id(copy_param_d[0])) print("======================") # 更改列表中元素的值 print("驗證修改可變數據類型元素的值") param_d[0].append(4) print(param_d) print(copy_param_d) param_d.append("abc") print(param_d) print(copy_param_d) param_d[1] = 19 print(param_d) print(copy_param_d) # 運行結果 驗證列表中元素 4534525792 4534525792 4537357968 4537357968 驗證修改可變數據類型元素的值 [[2, 3, 4], 18, 'paramD'] [[2, 3, 4], 18, 'paramD'] [[2, 3, 4], 18, 'paramD', 'abc'] [[2, 3, 4], 18, 'paramD'] [[2, 3, 4], 19, 'paramD', 'abc'] [[2, 3, 4], 18, 'paramD']
咱們從上面結果能夠看出,對於可變數據結構,他們元素的內存地址沒有變化(以List爲例,至關於新生成一個List,而後將原來List中元素的值所有copy到新生成的List中),而修改其中的可變數據類型(好比:param_d[0]),copy對象也會同步修改(copy_param_d[0]);而修改不可變元素(好比:param_d[1]),並不會影響其copy對象(copy_param_d[1])。code
綜上咱們能夠得出以下結論(敲黑板,劃重點):對象
爲了方便你們理解,畫了內存地址的簡圖:遞歸
首先是不可變數據類型,由於其值的內存地址是不可變的,因此在內存中只有這一份:內存
其次是可變數據類型:字符串
一樣的,咱們仍是先來看例子(代碼基本和上面的保持一致,只是修改了深拷貝方法deepcopy):
def deep_copy_test(): """ 對深拷貝進行驗證 :return: """ """ 對淺copy進行驗證 :return: """ # 不可變數據類型 param_a = 17 param_b = "paramB" param_c = (18, "paramC") copy_param_a = copy.deepcopy(param_a) copy_param_b = copy.deepcopy(param_b) copy_param_c = copy.deepcopy(param_c) print("驗證不可變數據類型") print(id(param_a)) print(id(copy_param_a)) print(id(param_b)) print(id(copy_param_b)) print(id(param_c)) print(id(copy_param_c)) print("======================") # 可變數據類型 param_d = [[2, 3], 18, "paramD"] param_e = {"key1": 18, "key2": "paramE", "key3": [1, 2]} param_f = {18, "paramF"} copy_param_d = copy.deepcopy(param_d) copy_param_e = copy.deepcopy(param_e) copy_param_f = copy.deepcopy(param_f) print("驗證可變數據類型") print(id(param_d)) print(id(copy_param_d)) print(id(param_e)) print(id(copy_param_e)) print(id(param_f)) print(id(copy_param_f)) print("======================") print("驗證列表中元素") # 驗證列表中第一個元素是否相等 print(id(param_d[1])) print(id(copy_param_d[1])) print(id(param_d[0])) print(id(copy_param_d[0])) print("======================") # 更改列表中元素的值 print("驗證修改可變數據類型元素的值") param_d[0].append(4) print(param_d) print(copy_param_d) param_d.append("abc") print(param_d) print(copy_param_d) param_d[1] = 19 print(param_d) print(copy_param_d) # 打印結果以下: 驗證不可變數據類型 4438175552 4438175552 4440636208 4440636208 4440885840 4440885840 ====================== 驗證可變數據類型 4440987760 4441335360 4440593344 4440594224 4440966160 4440967840 ====================== ====================== 驗證列表中元素 4438175584 4438175584 4440628192 4441336000 驗證修改可變數據類型元素的值 [[2, 3, 4], 18, 'paramD'] [[2, 3], 18, 'paramD'] [[2, 3, 4], 18, 'paramD', 'abc'] [[2, 3], 18, 'paramD'] [[2, 3, 4], 19, 'paramD', 'abc'] [[2, 3], 18, 'paramD']
咱們能夠和淺拷貝的運行結果作個對比,其中有差異的地方是:淺拷貝時列表中元素的內存地址沒變,而深拷貝時列表中元素的內存地址發生了變化(主要針對可變數據類型,好比:param_d[0]和copy_param_d[0])。另外,對於可變數據類型,修改原始數據中的值,並不會影響拷貝數據。
綜上,咱們得出以下結論(敲黑板,劃重點):
爲了你們理解,一樣畫了一幅內存簡圖(主要是針對可變數據類型),能夠對比下和淺拷貝時內存簡圖的區別:
本文主要介紹了在Python3中內存的深拷貝和淺拷貝機制,你們能夠動手寫一下文中貼的Python代碼,這樣更能加深你的理解。總結來講,對於Python的不可變數據類型,深拷貝和淺拷貝的差異不大;主要區別是Python中的可變數據類型,深拷貝會對列表中的子元素進行遞歸拷貝處理,而淺拷貝則不會。