返回

搞清楚浅拷贝与深拷贝,再也不踩坑!

后端

在编程的世界里,"拷贝"是一个经常用到的操作,它能将一个对象复制一份出来。但在 Python 中,拷贝又分为"浅拷贝"和"深拷贝"两种类型,它们之间有微妙的区别,如果没理解清楚,用到了很可能会踩坑。

浅拷贝:复制"指针","指向"相同对象

浅拷贝,顾名思义,只拷贝了对象的引用(指针),即指向该对象内存地址的引用。也就是说,浅拷贝后的新对象与原对象指向了同一个内存地址,它们共用同一份数据。

为了方便理解,我们举个例子:

# 定义一个列表
list1 = [1, 2, 3]

# 浅拷贝列表
list2 = list1

此时,list2 就是list1的浅拷贝,它们指向了同一块内存空间。

内存地址 | 内容
---------|--------
0x123456 | [1, 2, 3]

深拷贝:复制"值","创建"新对象

深拷贝,则拷贝了对象及其所有的子对象,创建了一个完全独立的新对象。也就是说,深拷贝后的新对象和原对象拥有不同的内存地址,它们不共用同一份数据。

继续上面的例子,我们对list1进行深拷贝:

# 深拷贝列表
import copy
list3 = copy.deepcopy(list1)

此时,list3 就是list1的深拷贝,它们拥有不同的内存地址。

内存地址 | 内容
---------|--------
0x123456 | [1, 2, 3]
0x654321 | [1, 2, 3]

区别总结

下表总结了浅拷贝和深拷贝的区别:

特征 浅拷贝 深拷贝
指针 复制引用 复制值
内存地址 共用内存地址 创建新内存地址
更改影响 更改一个对象会影响另一个对象 更改一个对象不会影响另一个对象

实例对比

为了进一步理解,我们来看一个实际的例子。

假设我们有一个包含列表的字典:

my_dict = {
    "list1": [1, 2, 3],
    "list2": [4, 5, 6],
}

现在,我们分别对my_dict进行浅拷贝和深拷贝:

# 浅拷贝字典
shallow_copy = my_dict

# 深拷贝字典
import copy
deep_copy = copy.deepcopy(my_dict)

此时,shallow_copydeep_copy都是my_dict的拷贝,但它们的行为不同。

如果我们修改shallow_copylist1的值:

shallow_copy["list1"].append(4)

那么my_dict中的list1的值也会发生改变,这是因为shallow_copymy_dict指向了同一块内存空间。

内存地址 | 内容
---------|--------
0x123456 | [1, 2, 3, 4]

而如果我们修改deep_copylist1的值:

deep_copy["list1"].append(4)

那么my_dict中的list1的值不会发生改变,这是因为deep_copymy_dict指向了不同的内存空间。

内存地址 | 内容
---------|--------
0x123456 | [1, 2, 3]
0x654321 | [1, 2, 3, 4]

使用场景

浅拷贝和深拷贝在不同的情况下都有各自的用途:

  • 浅拷贝: 适用于对象本身的修改不会影响其他对象的情况,例如复制一个临时变量或只读数据。
  • 深拷贝: 适用于对象本身的修改可能会影响其他对象的情况,例如复制一个包含其他对象的复杂对象。

结语

理解浅拷贝和深拷贝的区别对于写出健壮可靠的 Python 代码至关重要。下次再使用拷贝操作时,请花点时间考虑是需要浅拷贝还是深拷贝,这将有助于避免不必要的错误和坑点。