Skip to content

Python 深拷贝实现方法详解 ​

引言:从"复印件"说起 ​

想象一个生活场景:你有一份非常重要的文件,需要交给同事填写。为了不弄丢原件,你会怎么做?

  • 浅拷贝:你直接把原件的"复印件"给了同事。但这份复印件有个问题——文件上贴了一张便利贴,便利贴还是原来的那张。同事在便利贴上写了字,你的原件上也能看到。
  • 深拷贝:你不仅复印了文件本身,还把便利贴也复印了一份新的。同事拿到的是完全独立的副本,无论他怎么修改,都不会影响你的原件。

Python 中的拷贝(Copy)正是如此:

  • 浅拷贝(Shallow Copy):只复制对象本身,对象内部的引用(比如列表里的元素、字典里的值)还是共享的。
  • 深拷贝(Deep Copy):递归地复制对象及其所有子对象,生成一个完全独立的新对象。

为什么需要深拷贝?看一个真实开发中的例子:

python
# 场景:员工信息管理
employee = {
    "name": "张三",
    "skills": ["Python", "SQL"],  # 列表是可变对象
    "address": {"city": "北京", "district": "朝阳区"}  # 字典也是可变对象
}

# 浅拷贝:只复制最外层
import copy
shallow_emp = copy.copy(employee)
shallow_emp["skills"].append("Java")  # 修改技能列表
print(employee["skills"])  # ['Python', 'SQL', 'Java'] —— 原对象被影响了!

# 深拷贝:完全独立
deep_emp = copy.deepcopy(employee)
deep_emp["skills"].append("Go")
print(employee["skills"])  # ['Python', 'SQL', 'Java'] —— 原对象安全

如果你希望"复制出来的对象和原对象完全无关",就需要深拷贝。本文将系统讲解 Python 中所有主流的深拷贝实现方式。


一、深拷贝 vs 浅拷贝:核心区别 ​

在深入各种实现方法之前,先彻底理解两者的区别。

1.1 内存结构对比 ​

python
import copy

# 原始对象:包含可变子对象
original = {
    "name": "李四",
    "hobbies": ["读书", "跑步"],
    "info": {"age": 25, "city": "上海"}
}

# 浅拷贝
shallow = copy.copy(original)
# 深拷贝
deep = copy.deepcopy(original)

print("=== 内存地址对比 ===")
print(f"original 地址: {id(original)}")
print(f"shallow 地址:  {id(shallow)}")  # 新地址,但内部子对象共享
print(f"deep 地址:     {id(deep)}")     # 全新地址,内部子对象也是新的

print(f"\noriginal['hobbies'] 地址: {id(original['hobbies'])}")
print(f"shallow['hobbies'] 地址:  {id(shallow['hobbies'])}")  # 相同!共享引用
print(f"deep['hobbies'] 地址:     {id(deep['hobbies'])}")     # 不同!独立副本

输出结果:

=== 内存地址对比 ===
original 地址: 2561234567890
shallow 地址:  2561234568000  # 新对象,但...
deep 地址:     2561234568110  # 全新对象

original['hobbies'] 地址: 2561234568220
shallow['hobbies'] 地址:  2561234568220  # 和 original 相同!
deep['hobbies'] 地址:     2561234568330  # 全新地址

1.2 行为差异验证 ​

python
import copy

original = {"tags": ["a", "b"], "meta": {"count": 1}}

shallow = copy.copy(original)
deep = copy.deepcopy(original)

# 修改浅拷贝的子对象
shallow["tags"].append("c")
shallow["meta"]["count"] = 999

print("原对象:", original)
# {'tags': ['a', 'b', 'c'], 'meta': {'count': 999}} —— 被污染了!

# 深拷贝不受影响
deep["tags"].append("d")
deep["meta"]["count"] = 888

print("深拷贝修改后,原对象:", original)
# {'tags': ['a', 'b', 'c'], 'meta': {'count': 999}} —— 不再变化

核心结论:

特性浅拷贝 (copy.copy)深拷贝 (copy.deepcopy)
最外层对象新对象新对象
内部可变子对象共享引用递归复制
修改子对象是否影响原对象是否
性能开销小大
适用场景对象内部全是不可变类型对象嵌套了可变类型

二、标准库方案:copy.deepcopy ​

copy.deepcopy 是 Python 标准库提供的官方深拷贝方案,也是最通用、最推荐的方式。

2.1 核心原理 ​

copy.deepcopy 的工作流程:

  1. 遍历对象的所有属性/元素
  2. 对每个子对象递归执行深拷贝
  3. 使用 memo 字典记录已拷贝的对象,处理循环引用
  4. 返回完全独立的新对象

2.2 基础用法与测试 ​

python
import copy

# ========== 测试 1:嵌套字典 ==========
original = {
    "user": {
        "name": "王五",
        "contacts": ["13800138000", "wangwu@example.com"]
    },
    "settings": {"theme": "dark", "notifications": True}
}

copied = copy.deepcopy(original)

# 修改拷贝对象的嵌套内容
copied["user"]["contacts"].append("13900139000")
copied["settings"]["theme"] = "light"

print("原对象:", original["user"]["contacts"])
# ['13800138000', 'wangwu@example.com'] —— 未受影响
print("拷贝对象:", copied["user"]["contacts"])
# ['13800138000', 'wangwu@example.com', '13900139000']

# ========== 测试 2:嵌套列表 ==========
matrix = [[1, 2, 3], [4, 5, 6]]
matrix_copy = copy.deepcopy(matrix)
matrix_copy[0][0] = 999

print("原矩阵:", matrix[0])  # [1, 2, 3] —— 未受影响
print("拷贝矩阵:", matrix_copy[0])  # [999, 2, 3]

# ========== 测试 3:自定义对象 ==========
class Address:
    def __init__(self, city, street):
        self.city = city
        self.street = street

    def __repr__(self):
        return f"Address({self.city}, {self.street})"

class Person:
    def __init__(self, name, address):
        self.name = name
        self.address = address

    def __repr__(self):
        return f"Person({self.name}, {self.address})"

person = Person("赵六", Address("深圳", "南山大道"))
person_copy = copy.deepcopy(person)

person_copy.address.city = "广州"
person_copy.name = "赵七"

print("原对象:", person)       # Person(赵六, Address(深圳, 南山大道))
print("拷贝对象:", person_copy) # Person(赵七, Address(广州, 南山大道))

2.3 处理循环引用 ​

copy.deepcopy 能自动处理循环引用(对象 A 引用对象 B,对象 B 又引用对象 A):

python
import copy

# 构造循环引用
a = {"name": "A"}
b = {"name": "B", "friend": a}
a["friend"] = b  # a 引用 b,b 引用 a

# 深拷贝不会死循环
a_copy = copy.deepcopy(a)
print(a_copy["friend"]["friend"] is a_copy)  # True,循环结构被正确复制

2.4 优缺点分析 ​

优点缺点
标准库自带,无需安装性能开销最大
支持几乎所有 Python 对象无法拷贝文件句柄、网络连接等系统资源
自动处理循环引用深度过深时可能栈溢出
可通过 __deepcopy__ 自定义行为对大型嵌套对象速度较慢

2.5 适用场景 ​

  • 通用场景,不确定对象内部结构时
  • 自定义类实例的拷贝
  • 需要处理循环引用的复杂数据结构

三、JSON 序列化法:json.dumps + json.loads ​

利用 JSON 序列化/反序列化实现深拷贝,是一种简单直接的"土办法"。

3.1 核心原理 ​

  1. json.dumps(obj):将对象序列化为 JSON 字符串(此时与原对象完全脱离)
  2. json.loads(json_str):将字符串解析回 Python 对象(生成全新对象)

3.2 代码示例与测试 ​

python
import json

# ========== 测试:嵌套字典 ==========
original = {
    "name": "孙七",
    "scores": [90, 85, 92],
    "details": {
        "age": 28,
        "hobbies": ["游泳", "摄影"]
    }
}

# JSON 深拷贝
copied = json.loads(json.dumps(original))

# 修改拷贝对象
copied["scores"].append(100)
copied["details"]["hobbies"].append("旅行")

print("原对象:", original["scores"])       # [90, 85, 92]
print("拷贝对象:", copied["scores"])       # [90, 85, 92, 100]
print("原对象:", original["details"]["hobbies"])  # ['游泳', '摄影']
print("拷贝对象:", copied["details"]["hobbies"])  # ['游泳', '摄影', '旅行']

3.3 优缺点分析 ​

优点缺点
代码极简,一行搞定仅支持 JSON 兼容类型:dict, list, str, int, float, bool, None
天然跨语言(生成 JSON)不支持自定义类、datetime、set、tuple 等
不需要额外导入 copy 模块会丢失类型信息(如 tuple 变 list,int 键变 str 键)
性能通常比 deepcopy 好无法处理循环引用(会无限递归报错)

3.4 类型丢失演示 ​

python
import json

original = {
    1: "数字键",           # int 键
    (1, 2): "元组键",      # tuple 键 —— 会报错!JSON 不支持
    "data": (1, 2, 3),     # tuple 值
    "numbers": {1, 2, 3},  # set —— 会报错!JSON 不支持
}

# 尝试 JSON 拷贝会报错:
# TypeError: keys must be str, int, float, bool or None, not tuple

# 修正后的版本
original = {
    1: "数字键",           # int 键
    "data": (1, 2, 3),     # tuple 值
}

copied = json.loads(json.dumps(original))
print(type(list(copied.keys())[0]))  # <class 'str'> —— int 键变成了 str!
print(type(copied["data"]))          # <class 'list'> —— tuple 变成了 list!

3.5 适用场景 ​

  • 纯 JSON 数据(API 响应、配置文件)的拷贝
  • 需要跨语言/跨系统传输数据的场景
  • 对象结构简单,全是 JSON 兼容类型

四、序列化库进阶:pickle 与 msgpack ​

4.1 pickle:Python 专属序列化 ​

pickle 是 Python 专用的二进制序列化协议,支持几乎所有 Python 对象。

python
import pickle

# ========== 测试:自定义类 ==========
class Config:
    def __init__(self, name, values):
        self.name = name
        self.values = values

    def __repr__(self):
        return f"Config({self.name}, {self.values})"

original = Config("数据库配置", {"host": "localhost", "ports": [3306, 5432]})

# pickle 深拷贝
copied = pickle.loads(pickle.dumps(original))

copied.values["ports"].append(6379)
copied.name = "缓存配置"

print("原对象:", original)  # Config(数据库配置, {'host': 'localhost', 'ports': [3306, 5432]})
print("拷贝对象:", copied)  # Config(缓存配置, {'host': 'localhost', 'ports': [3306, 5432, 6379]})

pickle 的局限:

  • 仅 Python 可用,不跨语言
  • 存在安全风险(不要反序列化不可信数据)
  • 不同 Python 版本间可能不兼容

4.2 msgpack:高性能二进制序列化 ​

msgpack 是跨语言的二进制序列化格式,比 JSON 更快、更紧凑。

bash
pip install msgpack
python
import msgpack

# ========== 测试 ==========
original = {
    "name": "周八",
    "tags": ["admin", "developer"],
    "meta": {"level": 3, "active": True}
}

# msgpack 深拷贝
copied = msgpack.unpackb(msgpack.packb(original), raw=False)

copied["tags"].append("owner")
copied["meta"]["level"] = 5

print("原对象:", original["tags"])   # ['admin', 'developer']
print("拷贝对象:", copied["tags"])   # ['admin', 'developer', 'owner']
特性picklemsgpack
跨语言否(仅 Python)是(多语言支持)
速度中等快
支持类型几乎所有 Python 对象JSON 兼容类型 + bytes
安全性低(可执行任意代码)较高

五、第三方库方案 ​

5.1 ujson / orjson:更快的 JSON ​

标准库 json 的 C 语言加速版,API 兼容但速度更快。

bash
pip install ujson
# 或
pip install orjson
python
import ujson  # 或 import orjson

original = {"items": [1, 2, 3], "config": {"debug": False}}

# 用法与标准 json 完全一致
copied = ujson.loads(ujson.dumps(original))
copied["items"].append(4)

print("原对象:", original["items"])  # [1, 2, 3]
print("拷贝对象:", copied["items"])  # [1, 2, 3, 4]

orjson 额外优势:原生支持 datetime、UUID、numpy 数组等类型的序列化。

5.2 copyreg / __deepcopy__:自定义深拷贝行为 ​

当 copy.deepcopy 默认行为不满足需求时,可以为自定义类定义 __deepcopy__ 方法。

python
import copy

class DatabaseConnection:
    """模拟数据库连接:连接本身不可拷贝,但配置可以"""
    def __init__(self, host, credentials):
        self.host = host
        self.credentials = credentials  # 敏感信息
        self._connection = f"连接对象-{host}"  # 模拟不可拷贝的资源

    def __deepcopy__(self, memo):
        # 自定义深拷贝:不复制连接,重新建立
        new_obj = DatabaseConnection(
            host=self.host,
            credentials=copy.deepcopy(self.credentials, memo)
        )
        memo[id(self)] = new_obj
        return new_obj

original = DatabaseConnection("db.example.com", {"user": "admin", "pass": "secret"})
copied = copy.deepcopy(original)

print(copied.host)           # db.example.com
print(copied._connection)    # 连接对象-db.example.com(重新创建,不是拷贝)
print(copied.credentials is not original.credentials)  # True

六、手动实现:递归拷贝 ​

理解原理后,可以手写一个简易深拷贝,加深理解。

python
def manual_deepcopy(obj, memo=None):
    """
    手动实现简易深拷贝

    参数:
        obj: 要拷贝的对象
        memo: 用于处理循环引用的备忘录字典
    """
    if memo is None:
        memo = {}

    # 已拷贝过,直接返回(处理循环引用)
    obj_id = id(obj)
    if obj_id in memo:
        return memo[obj_id]

    # 不可变类型直接返回
    if isinstance(obj, (int, float, str, bool, type(None))):
        return obj

    # 列表
    if isinstance(obj, list):
        new_list = []
        memo[obj_id] = new_list
        for item in obj:
            new_list.append(manual_deepcopy(item, memo))
        return new_list

    # 字典
    if isinstance(obj, dict):
        new_dict = {}
        memo[obj_id] = new_dict
        for key, value in obj.items():
            new_dict[manual_deepcopy(key, memo)] = manual_deepcopy(value, memo)
        return new_dict

    # 集合
    if isinstance(obj, set):
        new_set = set()
        memo[obj_id] = new_set
        for item in obj:
            new_set.add(manual_deepcopy(item, memo))
        return new_set

    # 其他类型(简化处理,实际应更完善)
    return obj

# 测试
original = {"a": [1, 2, {"b": [3, 4]}]}
copied = manual_deepcopy(original)
copied["a"][2]["b"].append(5)

print("原对象:", original["a"][2]["b"])  # [3, 4]
print("拷贝对象:", copied["a"][2]["b"])  # [3, 4, 5]

注意:这只是教学示例,生产环境请使用 copy.deepcopy,它处理了更多边界情况。


七、常见坑点与注意事项 ​

7.1 性能陷阱 ​

深拷贝是递归操作,对象嵌套越深、数据量越大,性能开销越大。

python
import copy
import time
import json

# 构造大型嵌套对象
large_data = {"level": 0}
current = large_data
for i in range(1000):
    current["child"] = {"level": i + 1, "data": list(range(100))}
    current = current["child"]

# 测试 deepcopy 耗时
start = time.time()
copy1 = copy.deepcopy(large_data)
print(f"copy.deepcopy: {time.time() - start:.3f}秒")

# 测试 json 耗时(如果适用)
start = time.time()
copy2 = json.loads(json.dumps(large_data))
print(f"json 拷贝: {time.time() - start:.3f}秒")

优化建议:

  • 如果对象内部全是不可变类型,用浅拷贝即可
  • 考虑只深拷贝需要修改的部分
  • 使用 __deepcopy__ 自定义精简拷贝逻辑

7.2 不可拷贝的对象 ​

以下对象无法深拷贝,会抛出 TypeError:

python
import copy
import threading

# 文件对象
with open("test.txt", "w") as f:
    try:
        copy.deepcopy(f)
    except TypeError as e:
        print(f"文件对象不可拷贝: {e}")

# 线程锁
lock = threading.Lock()
try:
    copy.deepcopy(lock)
except TypeError as e:
    print(f"线程锁不可拷贝: {e}")

# 生成器
gen = (x for x in range(10))
try:
    copy.deepcopy(gen)
except TypeError as e:
    print(f"生成器不可拷贝: {e}")

7.3 循环引用导致的问题 ​

虽然 copy.deepcopy 能处理循环引用,但 JSON 方法会失败:

python
import json

a = {"name": "A"}
b = {"name": "B", "ref": a}
a["ref"] = b

# JSON 方法会无限递归
try:
    json.dumps(a)
except ValueError as e:
    print(f"JSON 无法处理循环引用: {e}")

# deepcopy 可以
import copy
c = copy.deepcopy(a)  # 正常工作

7.4 类型信息丢失 ​

JSON 方法会丢失类型信息:

原类型JSON 拷贝后
tuplelist
set报错(不支持)
datetime报错(不支持)
int 字典键str 键
自定义类报错(不支持)

7.5 深拷贝不是万能的 ​

深拷贝解决的是"对象图独立"问题,但以下情况仍需注意:

python
import copy

# 场景 1:类属性(类级别共享)
class Config:
    defaults = {"timeout": 30}  # 类属性,所有实例共享

c1 = Config()
c2 = copy.deepcopy(c1)
c2.defaults["timeout"] = 60
print(Config.defaults)  # {'timeout': 60} —— 类属性被改了!

# 场景 2:单例模式
class Singleton:
    _instance = None
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

s1 = Singleton()
s2 = copy.deepcopy(s1)
print(s1 is s2)  # True —— deepcopy 对 __new__ 有特殊处理

八、方案选型速查表 ​

场景推荐方案理由
通用场景,对象结构未知copy.deepcopy最全面,标准库支持
纯 JSON 数据(API/配置)json.loads/dumps简单快速,天然跨语言
自定义类实例copy.deepcopy支持 __deepcopy__ 自定义
需要跨语言传输msgpack / json序列化格式通用
高性能要求,纯数据orjson / msgpackC 实现,速度最快
包含循环引用copy.deepcopy唯一能安全处理循环引用
教学/理解原理手动实现深入理解递归和 memo 机制

九、总结 ​

Python 深拷贝的核心要点:

  1. copy.deepcopy 是首选:标准库、通用、安全,能处理循环引用和自定义类
  2. JSON 法适合简单数据:纯 JSON 兼容类型时,代码更简洁、性能更好
  3. pickle/msgpack 适合特定场景:需要序列化存储或跨语言传输时
  4. 性能敏感场景要谨慎:深拷贝是 O(n) 递归操作,大对象要考虑优化

记住一个原则:只在必要时深拷贝。如果对象内部全是不可变类型(数字、字符串、元组),浅拷贝就足够了。


本文示例代码均基于 Python 3.8+ 测试通过,可直接复制运行。第三方库需先通过 pip install 安装。