Python字典的浅拷贝与深拷贝


Tags: #Python
Created:
Updated:

前言

前一篇讲到的是内存空间的引用,而这个知识点跟字典的拷贝息息相关,所以顺便讲下

字典的拷贝同样遵循数据类型的规则,是采用引用,还是新建立对象 而这其中涉及的差别也就是人们常说的深拷贝浅拷贝

方法

赋值

赋值的话,我们以为我们创建的是新的副本,实则不是

赋值操作创建了对象的一个新的引用。
当你对一个变量进行赋值操作时,你创建了对已存在对象的一个新的引用,而不是创建了一个新的对象。

示例

a = {"key1": [1, 2, 3], "key2": {"nested": 42}, "key3": "123"}

# 将a的值赋值给b
b = a

# 修改key3的值
a["key3"] = "456"

print(a)  # {'key1': [1, 2, 3], 'key2': {'nested': 42}, 'key3': '456'}
print(b)  # {'key1': [1, 2, 3], 'key2': {'nested': 42}, 'key3': '456'}

# 修改key1的值
a["key1"].append(4)

print(a)  # {'key1': [1, 2, 3, 4], 'key2': {'nested': 42}, 'key3': '456'}
print(b)  # {'key1': [1, 2, 3, 4], 'key2': {'nested': 42}, 'key3': '456'}

可以发现怎么更改ab都随着改变
因为b=a仅仅是建立了对象的引用,也就是ab同时引用了字典对象而已

所以赋值不能算是拷贝❗❗❗

赋值(引用)不是拷贝,赋值(引用)不是拷贝,赋值(引用)不是拷贝
要记住这点,这是引用,不是拷贝❗❗❗

这仅仅是引用,不算拷贝

copy

字典有个copy方法,该方法创建并返回字典的浅拷贝。

示例

a = {"key1": [1, 2, 3], "key2": {"nested": 42}, "key3": "123"}

# 使用 copy 方法创建一个浅拷贝
b = a.copy()

# 修改key3的值
a["key3"] = "456"

print(a)  # {'key1': [1, 2, 3], 'key2': {'nested': 42}, 'key3': '456'}
print(b)  # {'key1': [1, 2, 3], 'key2': {'nested': 42}, 'key3': '123'}

# 修改key1的值
a["key1"].append(4)

print(a)  # {'key1': [1, 2, 3, 4], 'key2': {'nested': 42}, 'key3': '456'}
print(b)  # {'key1': [1, 2, 3, 4], 'key2': {'nested': 42}, 'key3': '123'}

可以发现,我们尝试修改了a变量 key3的值(不可变数据类型),b变量没有跟着改变

我们修改a变量 key1的值(可变数据类型),b变量跟着改变了

这就是上面说过的,要看数据类型是否可变才知道是采用引用还是新建立对象
由于key3的值原先是不可变数据类型,所以只能新建立对象
而由于key1的值是可变数据类型,所以是对[1, 2, 3]对象的引用,而非建立新对象

解包

Python 3.5开始,可以使用双星号 ** 来解包字典,从而创建一个新的字典(浅拷贝)。
相比较copy个人感觉这个更有范🙃,作用与copy相同,只是写法不同

a = {"key1": [1, 2, 3], "key2": {"nested": 42}, "key3": "123"}

# 使用 copy 方法创建一个浅拷贝
b = {**a}

# 修改key3的值
a["key3"] = "456"

print(a)  # {'key1': [1, 2, 3], 'key2': {'nested': 42}, 'key3': '456'}
print(b)  # {'key1': [1, 2, 3], 'key2': {'nested': 42}, 'key3': '123'}

# 修改key1的值
a["key1"].append(4)

print(a)  # {'key1': [1, 2, 3, 4], 'key2': {'nested': 42}, 'key3': '456'}
print(b)  # {'key1': [1, 2, 3, 4], 'key2': {'nested': 42}, 'key3': '123'}

deepcopy

上面提到的都是浅拷贝,那如果想要建立一个全新的字典副本,该怎么做?
这时候就得使用深拷贝了

使用内置模块 copydeepcopy 方法

import copy

a = {"key1": [1, 2, 3], "key2": {"nested": 42}, "key3": "123"}

# 使用 deepcopy 方法创建一个深拷贝
b = copy.deepcopy(a)

# 修改key3的值
a["key3"] = "456"

print(a)  # {'key1': [1, 2, 3], 'key2': {'nested': 42}, 'key3': '456'}
print(b)  # {'key1': [1, 2, 3], 'key2': {'nested': 42}, 'key3': '123'}

# 修改key1的值
a["key1"].append(4)

print(a)  # {'key1': [1, 2, 3, 4], 'key2': {'nested': 42}, 'key3': '456'}
print(b)  # {'key1': [1, 2, 3], 'key2': {'nested': 42}, 'key3': '123'}

可以发现ab是2个互不影响的字典,a做出改变,b不会做出改变

总结

  • 概念
    • 引用:可变数据、不可变数据对象均引用
    • 深拷贝:可变数据、不可变数据对象均建立新副本
    • 浅拷贝:可变数据使用对象的引用(如列表、字典等),不可变数据建立新副本
  • 引用
    • 赋值
  • 深拷贝:
    • deepcopy()
  • 浅拷贝
    • copy()
    • 解包