python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python深浅拷贝

一文带你掌握Python中的深浅拷贝

作者:小庄-Python办公

本文将深入解析Python中的深浅拷贝问题,通过实例演示赋值,浅拷贝和深拷贝的区别,这篇文章剖析了字典.copy()方法的误区,并给出最佳实践建议,希望对大家有所帮助

第一章:一切从“赋值”引发的问题开始

在 Python 的世界里,变量不仅仅是存储数据的标签,更像是指向内存中某个对象的“指针”。很多初学者,甚至是有一定经验的开发者,都曾在深浅拷贝的问题上栽过跟头。

想象一个场景:你有一个列表,里面包含了一些子列表,你想复制一份用来做修改,保留原数据。于是你顺手写了这样一行代码:

original_list = [[1, 2], [3, 4]]
new_list = original_list

# 现在,我想修改 new_list 的第一个子列表
new_list[0][0] = 999

print("Original:", original_list)
print("New:", new_list)

如果你的预期是 Original 保持 [[1, 2], [3, 4]],而 New 变为 [[999, 2], [3, 4]],那么很遗憾,现实会给你沉重一击。运行结果是:

Original: [[999, 2], [3, 4]]
New: [[999, 2], [3, 4]]

为什么?

这就是 Python 中最基础但也最容易被忽视的概念:赋值(Assignment)并不是拷贝

在上面的代码中,new_list = original_list 并没有创建一个新的列表对象,它仅仅是创建了一个新的引用(reference)。这就好比你有两个名字(original_listnew_list),但它们都指向同一个实体(内存中的列表对象)。因此,通过任何一个名字去修改这个实体,另一个名字看到的自然也是修改后的样子。

为了彻底解决这个问题,我们需要深入理解 Python 内存模型中的三个层次:赋值、浅拷贝和深拷贝

第二章:浅拷贝(Shallow Copy)——“只复制第一层”

当我们意识到直接赋值不是复制时,我们通常会转向浅拷贝。在 Python 中,实现浅拷贝的方法有很多:

让我们看看浅拷贝的表现:

import copy

original_list = [[1, 2], [3, 4]]
shallow_copied_list = copy.copy(original_list)

# 修改外层
shallow_copied_list.append([5, 6])

# 修改内层(嵌套对象)
shallow_copied_list[0][0] = 888

print("Original:", original_list)
print("Shallow:", shallow_copied_list)

输出结果:

Original: [[888, 2], [3, 4]]
Shallow: [[888, 2], [3, 4], [5, 6]]

分析:

什么是浅拷贝?

浅拷贝(Shallow Copy)会创建一个新的容器对象,但不会递归地复制容器内的元素。新容器中填充的是原容器中元素的引用

对于不可变对象(如整数、字符串、元组),引用就引用吧,反正改不了。但如果你的列表里包含了可变对象(如列表、字典、集合),那么这些可变对象的引用被共享了,这就是所谓的“共享子对象”(Shared Sub-objects)。

适用场景:浅拷贝适用于你的数据结构是“扁平”的,或者你明确知道你需要共享子对象(这很少见)。

第三章:深拷贝(Deep Copy)——“斩断所有羁绊”

如果你需要一个完全独立的副本,无论嵌套多少层,修改副本都不影响原件,那么你需要的是深拷贝

深拷贝使用 copy.deepcopy() 实现:

import copy

original_list = [[1, 2], [3, 4]]
deep_copied_list = copy.deepcopy(original_list)

# 彻底修改副本
deep_copied_list[0][0] = 777
deep_copied_list.append([9, 0])

print("Original:", original_list)
print("Deep:", deep_copied_list)

输出结果:

Original: [[1, 2], [3, 4]]
Deep: [[777, 2], [3, 4], [9, 0]]

什么是深拷贝?

深拷贝会递归地遍历原对象的所有子对象,并创建它们的副本。这意味着新对象和原对象在内存中是完全独立的,没有任何引用重叠。

深拷贝的陷阱与高级用法:

虽然深拷贝很强大,但它也有代价(性能开销大)和陷阱。

递归引用导致死循环

如果一个对象直接或间接引用了自己,deepcopy 会抛出 RecursionError

a = [1]
a.append(a) # a 现在是 [1, [...]]
# b = copy.deepcopy(a) # 这会报错

自定义类的拷贝控制:如果你需要控制类的深拷贝行为,可以实现 __deepcopy__ 方法。这在处理数据库连接、文件句柄等不可序列化或不可拷贝的资源时非常有用。

性能考量:对于巨大的数据结构,深拷贝可能非常慢。如果你的嵌套层级很浅,或者全是不可变数据,深拷贝就是杀鸡用牛刀。

第四章:核心原理图解与常见误区

为了彻底理清关系,我们可以通过一张简化的内存示意图来理解:

假设 a = [1, [2, 3]]

赋值 (b = a):

浅拷贝 (b = copy.copy(a)):

深拷贝 (b = copy.deepcopy(a)):

常见误区:字典的copy()方法

很多 Python 开发者会直接用字典自带的 .copy() 方法,认为这就是深拷贝。

错误! 字典的 .copy() 也是浅拷贝!

d1 = {'a': [1, 2]}
d2 = d1.copy()
d2['a'].append(3)

print(d1) # 输出 {'a': [1, 2, 3]},d1 被修改了!

正确的做法依然是 copy.deepcopy(d1) 或者使用 d1.copy() 配合字典推导式(如果只有一层的话)。

第五章:总结与最佳实践

搞懂了深浅拷贝,我们其实是在搞懂 Python 的对象引用模型。这是编写健壮、无副作用代码的基石。

最后的建议:

以上就是一文带你掌握Python中的深浅拷贝的详细内容,更多关于Python深浅拷贝的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文