面試題-python 淺拷貝和深拷貝(copy模塊)


前言

面試的時候經常會問到深拷貝和淺拷貝,那么python的深拷貝和淺拷貝有什么區別呢?

思考題

先來看 2 個簡單的案例, 對元素 a/aa 重新賦值一個新的變量 b/bb 后,改變原來 a/aa 的值,看會不會影響新的變量 b/bb 的值

# 1.str
a = "hello"
b = a
a = "world"
print('a: {}'.format(a))
print('b: {}'.format(b))

# 2.list
aa = [1, 2, 3]
bb = aa
aa.append(4)
print('aa: {}'.format(aa))
print('bb: {}'.format(bb))

運行結果

a: world
b: hello
aa: [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 = 100
d = 100
print(id(c))
print(id(d))
print(c is d)

運行結果

1557212603592
1557212603592
True
1561032832
1561032832
True

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)

運行結果

1638920310144
1638920310216
False
True
1638921292360
1638921292680
False
True

現在知道了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 copy


a = [1, "hello", [2, 3], {"key": "123"}]

b = copy.copy(a)

print(id(a))    # 外面容器拷貝了,所以a和b的id不一樣
print(id(b))

# a和b容器里面的元素對象id
print(id(a[2]))
print(id(b[2]))

運行結果

1340977220424
1340977221576
1340977220168
1340977220168

淺拷貝是拷貝了list外面一層的, 創建一個新的容器對象(compound object),所以a和b的id是不一樣的
對於容器里面的元素對象,淺拷貝就只會使用原始元素的引用(內存地址),所以可以看到子元素的內存地址還是一樣的

如果改變a里面的不可變對象數字和字符串,此時a和b的值就不一樣了,但是b的后面沒改變的元素還是指向a

# 改變a的 數字和字符串對象
a[0] = 2

# a 和b 的值不一樣了
print(a)
print(b)

# 但是后面的元素還是指的a
print(id(a[2]))
print(id(b[2]))

運行結果

[2, 'hello', [2, 3], {'key': '123'}]
[1, 'hello', [2, 3], {'key': '123'}]
2488134044232
2488134044232

如果改變a里面的可變對象, 把[2, 3]里面的3改成 [2, 4]

# 改變a的 可變對象 [2, 4]
a[2][1] = 4

print(a)
print(b)

print(id(a[2]))
print(id(b[2]))

運行結果

[1, 'hello', [2, 4], {'key': '123'}]
[1, 'hello', [2, 4], {'key': '123'}]
2385125673544
2385125673544

此時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容器里面的元素對象id
print(id(a[2]))
print(id(b[2]))

# 改變a的 可變對象 [2, 4]
a[2][1] = 4

print(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 = a
print(id(a))
print(id(b))

# a和b容器里面的元素對象id
print(id(a[2]))
print(id(b[2]))

a[0] = 2
print(a)
print(b)

運行結果

1992198687560
1992198687560
1992198687304
1992198687304
[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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM