前言

在Python编程中,你是否遇到过这样的状况:明明新建了一个变量 B,并把变量 A 的值赋给了它,结果修改 B 的时候,A 竟然也莫名其妙地跟着变了? 如果你踩过这个坑,那么恭喜你,你已经触碰到了 Python 中最常让人迷惑的知识点之一:引用赋值、浅拷贝(Shallow Copy)与深拷贝(Deep Copy)。 今天,我们就来扒一扒它们的底裤,把这个知识点彻底嚼碎。

一、为什么会产生拷贝问题?一切皆因“引用”

Python中,有一句名言:“万物皆对象”。当你创建一个变量并给它赋值时,你并没有真正把数据“装进”变量里,而是给这个数据贴上了一个标签(引用)

1.1 直接赋值(赋值的本质是共享)

直接使用 = 号进行赋值,本质上是让多个变量指向了内存中的同一个对象。

# 直接赋值示例
list_a = [1, 2, 3]
list_b = list_a  # list_b 只是 list_a 的另一个名字(引用)

list_b.append(4)

print("list_a:", list_a)  # 输出: [1, 2, 3, 4]
print("list_b:", list_b)  # 输出: [1, 2, 3, 4]
print(id(list_a) == id(list_b))  # 输出: True (内存地址完全一样)

结论: 赋值操作不会开辟新的内存空间,它只是给原有的数据多加了一个别名。修改其中一个,另一个必然跟着变。为了解决这个问题,我们需要真正的“拷贝”。

二、浅拷贝(Shallow Copy):只拷贝“第一层”

浅拷贝会创建一个新的对象,但对于对象内部包含的子对象(比如列表嵌套列表),它只拷贝子对象的引用

2.1 如何实现浅拷贝?

  • 使用内置库:import copy; b = copy.copy(a)
  • 使用切片操作(针对列表/元组):b = a[:]
  • 使用对象的内置方法:b = a.copy() (字典、列表可用)
  • 使用工厂函数:b = list(a), b = dict(a)

2. 2 浅拷贝的代码演示

我们来看一个包含“嵌套可变对象”的例子:

import copy

# 这是一个嵌套列表
original_list = [1, 2, [3, 4]]

# 进行浅拷贝
shallow_list = copy.copy(original_list)

# 场景 1:修改第一层的不可变元素(不影响原对象)
shallow_list[0] = 'X'
print("修改外层后 original_list:", original_list)  # [1, 2, [3, 4]]
print("修改外层后 shallow_list:", shallow_list)    # ['X', 2, [3, 4]]

# 场景 2:修改嵌套的内层列表(原对象跟着变了!)
shallow_list[2].append(5)
print("修改内层后 original_list:", original_list)  # [1, 2, [3, 4, 5]]
print("修改内层后 shallow_list:", shallow_list)    # ['X', 2, [3, 4, 5]]

原理解析: 浅拷贝就像是你建了一个新的大盒子,但大盒子里装的小盒子,依然是原来那个小盒子的“快捷方式”。你往小盒子里塞东西,原对象的小盒子也会多出同样的东西。

三、深拷贝(Deep Copy):彻头彻尾的独立克隆

深拷贝不仅创建一个新对象,还会递归地把对象内部的所有子对象都复制一遍。拷贝完成后,新对象和老对象在内存中完全独立,井水不犯河水。

3.1 如何实现深拷贝?

只能通过引入 copy 模块来实现:import copy; b = copy.deepcopy(a)

3.2 深拷贝的代码演示

同样是嵌套列表,我们看看深拷贝的表现:

import copy

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

# 进行深拷贝
deep_list = copy.deepcopy(original_list)

# 修改嵌套的内层列表
deep_list[2].append(5)

print("修改内层后 original_list:", original_list)  # 输出: [1, 2, [3, 4]] (原列表安然无恙!)
print("修改内层后 deep_list:", deep_list)          # 输出: [1, 2, [3, 4, 5]]

原理解析: 深拷贝会把大盒子、小盒子以及里面的所有东西,全部重新打造一份一模一样的。无论你怎么折腾新对象,都不会影响到原对象。

四、 核心总结与对比

为了方便记忆,我们可以用一张表来总结直接赋值、浅拷贝和深拷贝的区别:

操作类型 语法示例 内存地址(外层) 内存地址(内层可变对象) 是否相互影响?
直接赋值 b = a 相同 相同 牵一发而动全身,完全互相影响。
浅拷贝 b = copy.copy(a)b = a[:] 不同 相同 修改外层互不影响;修改内层嵌套对象,互相影响。
深拷贝 b = copy.deepcopy(a) 不同 不同 完全独立,彻底互不影响。

特别注意:关于不可变对象

如果你的对象里全部都是不可变元素(如数字、字符串、元组 (1, 2, 3)),那么浅拷贝和深拷贝的表现几乎是一样的。深拷贝和浅拷贝的真正区别,只有在面对可变对象(如列表、字典、集合)且存在多层嵌套时,才会显现出来。

结语

在日常的 Python 开发中:

  • 如果数据结构很简单(只有一层),直接使用 .copy() 浅拷贝即可,效率更高。
  • 如果在处理复杂的配置文件、多维数组或复杂的嵌套字典,且需要对其进行修改而不影响原数据,请务必果断使用 copy.deepcopy()

更多推荐