面試的時候經常會問到深拷貝和淺拷貝,那麼python的深拷貝和淺拷貝有什麼區別呢?
思考題先來看 2 個簡單的案例, 對元素 a/aa 重新賦值一個新的變量 b/bb 後,改變原來 a/aa 的值,看會不會影響新的變量 b/bb 的值
# 1.stra = "hello"b = aa = "world"print('a: {}'.format(a))print('b: {}'.format(b))# 2.listaa = [1, 2, 3]bb = aaaa.append(4)print('aa: {}'.format(aa))print('bb: {}'.format(bb))
運行結果
a: worldb: helloaa: [1, 2, 3, 4]bb: [1, 2, 3, 4]
這是個很有趣的事情,字符串重新賦值給b後,改變原來a的值,b不會跟著變。
但是list重新賦值給bb後,改變aa的值,bb的值也跟著變了。
這裡有個知識點:在python中,都是將「對象的引用(內存地址)」賦值給變量的。其次,在python中有6個標準數據類型,他們分為可變和不可變兩類。
在python中有6個標準數據類型,他們分為可變和不可變兩類。
不可變類型:Number(數字)String(字符串)Tuple(元組)可變類型:List(列表)Dictionary(字典)Set(集合)可變對象和不可變對象的內存地址可以通過id函數獲取
可變對象:可變對象可以在其 id() 保持固定的情況下改變其取值;不可變對象:具有固定值的對象。不可變對象包括數字、字符串和元組。這樣的對象不能被改變。如果必須存儲一個不同的值,則必須創建新的對象。id(object):函數用於獲取對象的內存地址,函數返回對象的唯一標識符,標識符是一個整數。字符串和數字都是不可變類型,不同變量賦值一樣,通過id獲取的內存地址是一樣的
# 作者-上海悠悠 QQ交流群:717225969# blog地址 https://www.cnblogs.com/yoyoketang/a = "abc"b = "abc"print(id(a))print(id(b))print(a is b)c = 100d = 100print(id(c))print(id(d))print(c is d)
運行結果
15572126035921557212603592True15610328321561032832True
list、dict 和 set集合是可變類型,雖然值一樣,但是id獲取的內存地址不一樣
# 作者-上海悠悠 QQ交流群:717225969# blog地址 https://www.cnblogs.com/yoyoketang/a = {"key": "123"}b = {"key": "123"}print(id(a))print(id(b))print(a is b)print(a == b)c = [1, 2, 3]d = [1, 2, 3]print(id(c))print(id(d))print(c is d)print(c == d)
運行結果
16389203101441638920310216FalseTrue16389212923601638921292680FalseTrue
現在知道了id函數獲取內存地址,我們說的深拷貝和淺拷貝是針對可變對象:list、dict 和 set集合
copy模塊python 中的深拷貝和淺拷貝使用 copy 模塊
淺拷貝 A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
上面這段話是官方文檔上的描述,有2個含義:
1.淺拷貝會創建一個新的容器對象(compound object)2.對於對象中的元素,淺拷貝就只會使用原始元素的引用(內存地址)常見的淺拷貝操作有:
使用切片操作[:]使用工廠函數(如list/dict/set)copy模塊的copy()方法深拷貝 A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.
上面這段話是官方文檔上的描述,也是有2個含義:
1.深拷貝和淺拷貝一樣,都會創建一個新的容器對象(compound object)2.和淺拷貝的不同點在於,深拷貝對於對象中的元素,深拷貝都會重新生成一個新的對象淺拷貝淺拷貝使用 copy 模塊的 copy 方法
# 作者-上海悠悠 QQ交流群:717225969# blog地址 https://www.cnblogs.com/yoyoketang/import copya = [1, "hello", [2, 3], {"key": "123"}]b = copy.copy(a)print(id(a)) # 外面容器拷貝了,所以a和b的id不一樣print(id(b))# a和b容器裡面的元素對象idprint(id(a[2]))print(id(b[2]))
運行結果
1340977220424134097722157613409772201681340977220168
淺拷貝是拷貝了list外面一層的, 創建一個新的容器對象(compound object),所以a和b的id是不一樣的
對於容器裡面的元素對象,淺拷貝就只會使用原始元素的引用(內存地址),所以可以看到子元素的內存地址還是一樣的
如果改變a裡面的不可變對象數字和字符串,此時a和b的值就不一樣了,但是b的後面沒改變的元素還是指向a
# 改變a的 數字和字符串對象a[0] = 2# a 和b 的值不一樣了print(a)print(b)# 但是後面的元素還是指的aprint(id(a[2]))print(id(b[2]))
運行結果
[2, 'hello', [2, 3], {'key': '123'}][1, 'hello', [2, 3], {'key': '123'}]24881340442322488134044232
如果改變a裡面的可變對象, 把[2, 3]裡面的3改成 [2, 4]
# 改變a的 可變對象 [2, 4]a[2][1] = 4print(a)print(b)print(id(a[2]))print(id(b[2]))
運行結果
[1, 'hello', [2, 4], {'key': '123'}][1, 'hello', [2, 4], {'key': '123'}]23851256735442385125673544
此時b會隨著a的改變而改變,這就是淺拷貝了
深拷貝淺拷貝使用 copy 模塊的 deepcopy 方法
import copy# 作者-上海悠悠 QQ交流群:717225969# blog地址 https://www.cnblogs.com/yoyoketang/a = [1, "hello", [2, 3], {"key": "123"}]b = copy.deepcopy(a)print(id(a)) # 外面容器拷貝了,所以a和b的id不一樣print(id(b))# a和b容器裡面的元素對象idprint(id(a[2]))print(id(b[2]))# 改變a的 可變對象 [2, 4]a[2][1] = 4print(a)print(b)print(id(a[2]))print(id(b[2]))
深拷貝和淺拷貝的不同點在於,深拷貝對於對象中的元素,深拷貝都會重新生成一個新的對象。
所以不管a怎麼變,都不會影響b的值
賦值跟淺拷貝 深拷貝是有區別的,可以看下面的示例
# 作者-上海悠悠 QQ交流群:717225969# blog地址 https://www.cnblogs.com/yoyoketang/a = [1, "hello", [2, 3], {"key": "123"}]b = aprint(id(a))print(id(b))# a和b容器裡面的元素對象idprint(id(a[2]))print(id(b[2]))a[0] = 2print(a)print(b)
運行結果
1992198687560199219868756019921986873041992198687304[2, 'hello', [2, 3], {'key': '123'}][2, 'hello', [2, 3], {'key': '123'}]
賦值語句並沒有生成新的容器,跟淺拷貝的區別在於外面的容器也是指向的a的內存地址,並沒有生成新的容器
參考博客資料https://www.nowcoder.com/discuss/203654?type=2&order=0&pos=1232&page=0
參考博客資料https://copyfuture.com/blogs-details/2020031720252559878eggumgw4iaj7c
2021年第六期《python接口自動化+測試開發》課程,1月9號開學(火熱報名中!)
本期上課時間:1月9號-4月18號,每周六、周日晚上20:30-22:30
聯繫微信/QQ:283340479