Python怎么实现浅拷贝和深拷贝?

Ryan
Ryan
发布时间:2026-10-22 | 3人浏览过
原创

在 Python 中,直接使用等号赋值(如 b = a)并不会创建新的对象副本,而仅仅是将新变量名绑定到已有的内存对象上。要真正复制对象并脱离原始引用的影响,通常使用标准库的 copy 模块:通过 copy.copy() 实现浅拷贝,通过 copy.deepcopy() 实现深拷贝。浅拷贝只复制最外层的容器对象,其内部嵌套的子元素依然共享原有的内存引用;而深拷贝会自顶向下递归复制所有层级的嵌套对象,使得新旧两个数据结构彻底解耦、互不影响。

一、直接赋值与对象复制的本质区别

理解拷贝的前提是区分 Python 中的“变量绑定”与“对象创建”。在 Python 内部,变量名本质上是指向内存地址的引用指针。当执行简单的赋值语句时,Python 只是给目标对象贴上了一个新的标签,并未在内存中开辟新的存储空间。

# 示例:直接赋值只是共享同一个引用
original_list = [1, 2, [3, 4]]
assigned_list = original_list

# 修改新变量中的元素
assigned_list[0] = 99
assigned_list[2].append(5)

# 查看原列表与新列表
print("original_list:", original_list)
print("assigned_list:", assigned_list)
print("内存地址是否相同:", id(original_list) == id(assigned_list))
# 示例输出:
original_list: [99, 2, [3, 4, 5]]
assigned_list: [99, 2, [3, 4, 5]]
内存地址是否相同: True

从输出中可以看到,无论是修改第一层的数值还是修改第二层的子列表,原列表与新变量都保持同步变化,因为它们指向完全相同的内存地址。当我们需要一个可以独立修改的副本时,就必须使用真正的拷贝技术。

Python怎么实现浅拷贝和深拷贝?
图 1:赋值、浅拷贝与深拷贝在内存引用层面的核心机制对比

二、Python实现浅拷贝的常用方法

浅拷贝会构造一个新的复合对象,然后尽可能地将原始对象中各元素的“引用”插入到新对象中。实现浅拷贝有多种途径,具体根据操作的数据结构类型而定。

1. 使用 copy 模块的 copy() 函数

这是最标准、最通用的浅拷贝实现方法,适用于列表、字典、集合以及自定义类对象:

import copy

origin = [10, 20, [30, 40]]
shallow_copied = copy.copy(origin)

# 验证外层对象地址是否独立
print("外层ID相同:", id(origin) == id(shallow_copied))  # False

# 修改浅拷贝对象的第一层元素
shallow_copied[0] = 999
print("原列表第一层:", origin[0])          # 仍然是 10,互不干扰

# 修改浅拷贝对象的内层嵌套元素
shallow_copied[2].append(50)
print("原列表嵌套层:", origin[2])          # 变为 [30, 40, 50],发生了相互影响

2. 内置容器的快捷浅拷贝语法

对于常用的 Python 容器类型,语言本身提供了简捷的原生浅拷贝方式:

  • 列表切片:new_list = old_list[:]。通过全切片生成新的列表容器。
  • 工厂函数转换:new_list = list(old_list) 或 new_dict = dict(old_dict)。
  • 字典与集合的 copy 方法:new_dict = old_dict.copy() 或 new_set = old_set.copy()。
注意事项:以上所有内置语法都严格属于浅拷贝。如果容器内部只存放整数、浮点数、字符串、元组等不可变对象,浅拷贝完全够用且性能极高;但如果内部存在子列表、子字典等可变元素,修改内部子元素依然会污染原始数据。
Python怎么实现浅拷贝和深拷贝?
图 2:在 Python 项目中应用拷贝特性的标准实施流程

三、Python实现深拷贝的完整方法

当对象结构复杂,包含多层嵌套列表、嵌套字典或者对象属性之间存在层级关联时,必须使用 copy.deepcopy() 进行深拷贝。深拷贝会从外向内层层递归遍历,把遇到的每一个复合对象都创建出一份新的副本,并在新副本中建立与原对象相同的结构拓扑。

import copy

# 准备包含两层嵌套结构的复杂字典
user_data = {
    "name": "张三",
    "roles": ["editor", "reviewer"],
    "profile": {
        "city": "北京",
        "scores": [88, 92]
    }
}

# 执行深拷贝
deep_copied_user = copy.deepcopy(user_data)

# 修改深拷贝副本的深层嵌套数据
deep_copied_user["roles"].append("admin")
deep_copied_user["profile"]["city"] = "上海"
deep_copied_user["profile"]["scores"][0] = 100

# 检查原数据
print("原始 roles:", user_data["roles"])
print("原始 profile.city:", user_data["profile"]["city"])
print("原始 profile.scores:", user_data["profile"]["scores"])
# 示例输出:
原始 roles: ['editor', 'reviewer']
原始 profile.city: 北京
原始 profile.scores: [88, 92]

可以看到,无论嵌套多深,对深拷贝对象的修改都不会波及到原始字典,新旧数据在内存中彻底解耦。

深拷贝如何防止死循环(循环引用)?

在复杂数据结构中,常常出现对象自我引用或双向引用的情况(如 node.next = node)。如果采用普通的递归复制,会导致无限递归引发 RecursionError。copy.deepcopy() 在内部维护了一个名为 memo 的字典映射:

  • 当开始拷贝一个对象时,先检查其 id(obj) 是否已存在于 memo 中。
  • 如果已存在,说明该对象在此次递归链中已被复制过,直接返回之前创建的新副本。
  • 如果不存在,则先在 memo 中登记,再递归拷贝其内部子元素。这一机制从根本上杜绝了循环引用的死循环风险。
Python怎么实现浅拷贝和深拷贝?
图 3:copy.deepcopy 针对循环引用与层级依赖的 memo 注册机制

四、自定义类支持浅拷贝与深拷贝

在自定义类中,如果希望精确控制对象的拷贝行为(例如某些资源如网络连接不能拷贝、某些敏感字段在拷贝时重置),可以重写类的特殊魔法方法:

  • __copy__(self):定义调用 copy.copy(obj) 时的行为。
  • __deepcopy__(self, memo):定义调用 copy.deepcopy(obj, memo) 时的行为,必须接收并透传 memo 字典。
import copy

class ConnectionSession:
    def __init__(self, username, data, token):
        self.username = username
        self.data = data      # 可变列表
        self.token = token    # 临时会话凭证

    def __copy__(self):
        # 浅拷贝自定义实现:共享 data,保留原 token
        cls = self.__class__
        new_instance = cls.__new__(cls)
        new_instance.username = self.username
        new_instance.data = self.data
        new_instance.token = self.token
        return new_instance

    def __deepcopy__(self, memo):
        # 深拷贝自定义实现:递归拷贝 data,同时重置敏感 token
        cls = self.__class__
        new_instance = cls.__new__(cls)
        memo[id(self)] = new_instance
        new_instance.username = self.username
        new_instance.data = copy.deepcopy(self.data, memo)
        new_instance.token = None  # 独立副本安全置空
        return new_instance

session = ConnectionSession("admin", ["log1", "log2"], "auth_secret_998")
cloned_session = copy.deepcopy(session)

print("克隆对象 token:", cloned_session.token)
print("原数据与克隆数据是否解耦:", session.data is not cloned_session.data)
# 示例输出:
克隆对象 token: None
原数据与克隆数据是否解耦: True

五、开发中常见误区与选型总结

在工程实践中,选择浅拷贝还是深拷贝需要综合考量数据安全与执行效率:

  1. 不可变对象的单例优化:对于纯数字、字符串、以及仅含不可变元素的元组,Python 的 copy() 和 deepcopy() 都不会真正复制数据,而是直接返回原对象。这是因为不可变对象内容不可修改,共享引用绝对安全且节约内存。
  2. 深拷贝的性能开销:深拷贝涉及完整的对象树遍历、反射构造及 memo 查找,在面对数十万级别元素的大字典、复杂图网络或深层树状结构时,耗时与内存增长显著。若结构仅为一维,切勿无脑使用 deepcopy。
  3. 系统资源对象无法拷贝:如果对象中包含打开的本地文件句柄、Socket 网络套接字、数据库长连接或多线程锁(Lock),copy.deepcopy() 会抛出 TypeError: cannot pickle / cannot copy 错误。这类资源必须在自定义的 __deepcopy__ 中排除或重新显式初始化。
Python怎么实现浅拷贝和深拷贝?
图 4:浅拷贝与深拷贝技术选型与避坑决策树

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表