摘要:之前關于的作用域賦值參數傳遞,我們接連談了幾篇文章全菊變量和菊部變量關于函數參數傳遞,人都錯了可變對象與不可變對象今天我們依然要就相關話題繼續下去。這是由于它們是不可變對象,不存在被修改的可能,所以拷貝和賦值是一樣的。
之前關于 Python 的作用域、賦值、參數傳遞,我們接連談了幾篇文章:
全菊變量和菊部變量
關于函數參數傳遞,80%人都錯了
可變對象與不可變對象
今天我們依然要就相關話題繼續下去。
首先是上次最后的思考題:
m = [1, 2, [3]] n = m[:] n[1] = 4 n[2][0] = 5 print(m)
m 的結果是什么?
正確答案是 [1, 2, [5]] ,這次比上次好點,有 35% 的正確率。
當時我留了個提示,說和淺拷貝、深拷貝有關,現在我們就來具體說一說。
假設有這樣一個 list 變量 m,其中有 4 個元素(別被嵌套迷惑了):
m = [1, 2, [3, 4], [5, [6, 7]]]
為了更直觀的表示,我來畫個圖:
現在我們想要再來“復制”一個同樣的變量。也許第一個閃過腦中的念頭就是:
n = m
但看了前面的文章后你應該知道,這樣的 賦值只相當于增加了一個標簽,并沒有新的對象產生 :
用 id 驗證下就知道, m 和 n 仍然是同一個東西 。那么他們內部的元素自然也是一樣的,對其中一個進行修改,另一個也會跟著變:
m = [1, 2, [3, 4], [5, [6, 7]]] print("m:", id(m)) print([id(i) for i in m]) n = m print("n:", id(n)) print([id(i) for i in n]) print(n is m) print(n[0] is m[0]) print(n[2] is m[2]) n[0] = -1 print(m) n[2][1] = -1 print(m)
輸出
m: 4564554888 [4556507504, 4556507536, 4564554760, 4564555016] n: 4564554888 [4556507504, 4556507536, 4564554760, 4564555016] True True True [-1, 2, [3, 4], [5, [6, 7]]] [-1, 2, [3, -1], [5, [6, 7]]]
因此有人將此操作稱為“ 舊瓶裝舊酒 ”,只是多貼了一層標簽,這不能達到我們的目的。要得到一個對象的“拷貝”,我們需要用到 copy 方法:
from copy import copy m = [1, 2, [3, 4], [5, [6, 7]]] print("m:", id(m)) print([id(i) for i in m]) n = copy(m) print("n:", id(n)) print([id(i) for i in n]) print(n is m) print(n[0] is m[0]) print(n[2] is m[2]) n[0] = -1 print(m) n[2][1] = -1 print(m)
輸出
m: 4340253832 [4333009264, 4333009296, 4340253704, 4340253960] n: 4340268104 [4333009264, 4333009296, 4340253704, 4340253960] False True True [1, 2, [3, 4], [5, [6, 7]]] [1, 2, [3, -1], [5, [6, 7]]]
從結果中可以看出, n 和 m 已不是同一個對象 ,對于某個元素的重新賦值不會影響原對象。但是,它們 內部的元素全都是一樣的 ,所以對一個可變類型元素的修改,則仍然會反應在原對象中。
(其實這里1、2也是指向同一個對象,但作為不可變對象來說,它們互不影響,直觀上的感受就相當于是復制了一份,故簡化如圖上所示)
這種復制方法叫做 淺拷貝 ( shallow copy ),又被人形象地稱作“ 新瓶裝舊酒 ”,雖然產生了新對象,但里面的內容還是來自同一份。
如果要徹底地產生一個和原對象完全獨立的復制品,得使用 深拷貝 ( deep copy ):
from copy import deepcopy m = [1, 2, [3, 4], [5, [6, 7]]] print("m:", id(m)) print([id(i) for i in m]) n = deepcopy(m) print("n:", id(n)) print([id(i) for i in n]) print(n is m) print(n[0] is m[0]) print(n[2] is m[2]) n[0] = -1 print(m) n[2][1] = -1 print(m)
輸出
m: 4389131400 [4381886832, 4381886864, 4389131272, 4389131528] n: 4389131208 [4381886832, 4381886864, 4389131656, 4389145736] False True False [1, 2, [3, 4], [5, [6, 7]]] [1, 2, [3, 4], [5, [6, 7]]]
此時, 對新對象中元素做任何改動都不會影響原對象 。新對象中的子列表,無論有多少層,都是新的對象,有不同的地址。
按照前面的比喻,深拷貝就是“ 新瓶裝新酒 ”。
你可能會注意到一個細節:n 中的前兩個元素的地址仍然和 m 中一樣。這是由于它們是 不可變對象,不存在被修改的可能,所以拷貝和賦值是一樣的 。
于是,深拷貝也可以理解為,不僅是對象自身的拷貝,而且對于對象中的每一個子元素,也都進行同樣的拷貝操作。這是一種 遞歸 的思想。
不過額外要說提醒一下的是, 深拷貝的實現過程并不是完全的遞歸 ,否則如果對象的某級子元素是它自身的話,這個過程就死循環了。實際上, 如果遇到已經處理過的對象,就會直接使用其引用,而不再重復處理 。聽上去有點難懂是不是?想想這個例子大概就會理解了:
from copy import deepcopy m = [1, 2] m.append(m) print(m, id(m), id(m[2])) n = deepcopy(m) print(n, id(n), id(n[2]))
輸出
[1, 2, [...]] 4479589576 4479589576 [1, 2, [...]] 4479575048 4479575048
最后,還是給各位留個思考:
from copy import deepcopy a = [3, 4] m = [1, 2, a, [5, a]] n = deepcopy(m) n[3][1][0] = -1 print(n)
深拷貝后的 n,修改了其中一個元素值,會是怎樣的效果?
思考一下輸出會是什么?
然后自己在電腦上或者我們的在線編輯器 Crossin的編程教室 - 在線Python編輯器 里輸入代碼運行下看看結果,再想想為什么。
歡迎留言給出你的解釋。
════
其他文章及回答:
如何自學Python | 新手引導 | 精選Python問答 | Python單詞表 | 人工智能 | 嘻哈 | 爬蟲 | 我用Python | 高考 | requests | AI平臺
歡迎搜索及關注: Crossin的編程教室
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44991.html
摘要:深拷貝和淺拷貝原始對象賦值,傳對象的引用對象拷貝,淺拷貝對象拷貝,深拷貝修改對象修改對象中的數組對象 深拷貝和淺拷貝 import copy a = [1, 2, 3, 4, [a, b]] #原始對象 b = a #賦值,傳對象的引用 c = copy.copy(a) #對象拷貝,淺拷貝 d = copy.deepcopy(a) #對象拷貝,深拷貝 a...
摘要:引用數據類型名存在棧內存中,值存在于堆內存中,但是棧內存會提供一個引用的地址指向堆內存中的值,我們以上面淺拷貝的例子畫個圖當進行拷貝時,其實復制的是的引用地址,而并非堆里面的值。 如何區分深拷貝與淺拷貝? 簡單來說,就是假設B復制了A,當修改A時,看B是否會發生變化,如果B也跟著變了,說明這是淺拷貝,拿人手短,如果B沒變,那就是深拷貝,自食其力。 淺拷貝例子: var a=[0,1...
摘要:中的深拷貝與淺拷貝說到深淺拷貝的時候就不得不說一下中的變量類型了基本類型按值存放在棧內存中的簡單數據段可以直接訪問引用類型存放在堆內存中的對象變量保存的是一個指向存放數據位置的指針訪問引用類型的值時首先從棧中獲取到存放該數據位置的指針然后再 JS中的深拷貝與淺拷貝 說到深淺拷貝的時候就不得不說一下JS中的變量類型了: 基本類型: undefined、null、boolean、numb...
摘要:期深拷貝與淺拷貝的區別如何實現一個深拷貝在回答這個問題前,我們先來回顧一下中兩大數據類型基本類型引用類型基本類型基本類型就是值類型存放在棧內存中的簡單數據段,數據大小確定,內存空間大小可以分配引用類型引用類型存放在堆內存中的對象,變量實際保 20190311期 深拷貝與淺拷貝的區別?如何實現一個深拷貝 在回答這個問題前,我們先來回顧一下JS中兩大數據類型 基本類型 Undefined...
閱讀 2404·2021-11-23 09:51
閱讀 1217·2021-11-22 13:54
閱讀 3427·2021-09-24 10:31
閱讀 1092·2021-08-16 10:46
閱讀 3627·2019-08-30 15:54
閱讀 707·2019-08-30 15:54
閱讀 2889·2019-08-29 17:17
閱讀 3162·2019-08-29 15:08