一文带你深入了解Python中的深拷贝与浅拷贝数据

 更新时间:2026年01月16日 08:50:14   作者:detayun  
在Python编程中,对象复制是一个常见但容易出错的操作,本文将系统讲解这两种拷贝方式的区别、应用场景及实现方法,感兴趣的小伙伴可以了解下

在Python编程中,对象复制是一个常见但容易出错的操作。许多开发者在处理可变对象(如列表、字典)时,常常会遇到"修改副本却影响了原对象"的困惑。这背后正是深拷贝和浅拷贝机制在起作用。本文将系统讲解这两种拷贝方式的区别、应用场景及实现方法。

一、基础概念:Python对象引用机制

在深入探讨拷贝前,必须理解Python的变量本质:

a = [1, 2, 3]
b = a  # 这并不是创建副本,而是创建新引用

b[0] = 99
print(a)  # 输出: [99, 2, 3]

这段代码揭示了Python的赋值本质:变量存储的是对象的引用,而非对象本身b = a只是让b指向与a相同的对象。

二、浅拷贝(Shallow Copy)

浅拷贝创建新对象,但不递归复制嵌套对象,而是复制嵌套对象的引用。

实现方式

  • 使用copy模块的copy()函数
  • 对象自带的拷贝方法(如列表的slice操作)
  • 特定类型的构造函数(如list(), dict()

示例1:列表的浅拷贝

import copy

original = [1, [2, 3], 4]
shallow_copy = copy.copy(original)

# 修改顶层元素
shallow_copy[0] = 100
print(original)    # [1, [2, 3], 4] - 不受影响

# 修改嵌套列表
shallow_copy[1][0] = 200
print(original)    # [1, [200, 3], 4] - 受影响!

示例2:字典的浅拷贝

original_dict = {'a': [1, 2], 'b': 3}
shallow_dict = original_dict.copy()  # 或 copy.copy(original_dict)

shallow_dict['a'][0] = 99
print(original_dict)  # {'a': [99, 2], 'b': 3} - 嵌套列表被修改

浅拷贝的内存结构

original: [obj1, [obj2, obj3], obj4]
           ^      ^
           |      |
shallow_copy: [obj1, [obj2, obj3], obj4]  # 嵌套列表是同一对象

三、深拷贝(Deep Copy)

深拷贝创建新对象,并递归复制所有嵌套对象,完全独立于原对象。

实现方式

唯一标准方法:copy.deepcopy()

import copy

original = [1, [2, 3], 4]
deep_copy = copy.deepcopy(original)

deep_copy[1][0] = 200
print(original)    # [1, [2, 3], 4] - 完全不受影响

深拷贝的内存结构

original: [obj1, [obj2, obj3], obj4]
           ^      ^
           |      |
deep_copy: [obj1, [obj5, obj6], obj4]  # 所有嵌套对象都是新创建的

四、关键区别对比

特性浅拷贝深拷贝
顶层对象新创建新创建
嵌套对象共享引用新创建副本
性能更快(不递归复制)较慢(需要递归处理)
内存占用较低较高
适用场景无嵌套或不需要独立嵌套对象时需要完全独立副本时

五、特殊情况处理

1. 不可变对象的拷贝

对于不可变类型(如数字、字符串、元组),浅拷贝和深拷贝效果相同:

import copy

t1 = (1, [2, 3])  # 注意:元组包含可变对象时行为特殊
t2 = copy.copy(t1)
t3 = copy.deepcopy(t1)

t2[1][0] = 99  # 仍然可以修改嵌套列表
print(t1)      # (1, [99, 3]) - 原元组受影响!

重要提醒:即使使用深拷贝,如果对象本身包含可变子对象(如元组中的列表),这些子对象仍然可以被修改!

2. 自定义对象的拷贝

对于自定义类,可以通过实现__copy__()__deepcopy__()方法控制拷贝行为:

class MyClass:
    def __init__(self, value):
        self.value = value
        self.nested = [1, 2, 3]
    
    def __copy__(self):
        print("执行浅拷贝")
        new_obj = MyClass(self.value)
        new_obj.nested = self.nested.copy()  # 手动浅拷贝嵌套列表
        return new_obj
    
    def __deepcopy__(self, memo):
        print("执行深拷贝")
        new_obj = MyClass(self.value)
        new_obj.nested = copy.deepcopy(self.nested, memo)  # 递归深拷贝
        return new_obj

obj = MyClass(10)
shallow = copy.copy(obj)
deep = copy.deepcopy(obj)

3. 循环引用的处理

深拷贝能正确处理对象间的循环引用:

a = [1, 2]
b = [a, 3]
a.append(b)  # 循环引用: a -> b -> a

c = copy.deepcopy(a)
print(c[0] is c[1][0])  # True - 保持引用关系
print(c is a)           # False - 新对象

六、实际应用场景

1. 何时使用浅拷贝

  • 需要高效复制大型对象但嵌套结构不需要独立时
  • 如复制包含大量数据的DataFrame,但只需要修改顶层属性
  • 游戏开发中复制对象状态但共享资源(如纹理、模型)

2. 何时使用深拷贝

  • 需要完全独立的对象副本时
  • 如配置对象的修改不应影响原始配置
  • 多线程环境中需要确保对象隔离
  • 机器学习中复制模型参数进行不同实验

3. 替代方案考虑

对于简单场景,可以考虑:

列表/字典推导式:创建部分独立副本

original = [1, [2, 3], 4]
partial_copy = [x if not isinstance(x, list) else x.copy() for x in original]

序列化/反序列化:通过JSON等格式实现深拷贝效果

import json
original = {'a': [1, 2], 'b': 3}
deep_copy = json.loads(json.dumps(original))

七、性能比较

测试不同拷贝方式的性能差异(对包含1000个元素的列表,其中每个元素是包含100个元素的列表):

import copy
import time

def create_nested_list(depth, size):
    if depth == 0:
        return 0
    return [create_nested_list(depth-1, size) for _ in range(size)]

big_list = create_nested_list(2, 1000)

# 测试浅拷贝
start = time.time()
for _ in range(100):
    shallow = copy.copy(big_list)
print(f"浅拷贝耗时: {(time.time()-start)*1000:.2f}ms")

# 测试深拷贝
start = time.time()
for _ in range(100):
    deep = copy.deepcopy(big_list)
print(f"深拷贝耗时: {(time.time()-start)*1000:.2f}ms")

典型结果

浅拷贝耗时: 15.32ms
深拷贝耗时: 1250.78ms

八、最佳实践总结

  • 默认使用浅拷贝:当不确定时,先尝试浅拷贝,仅在必要时使用深拷贝
  • 注意不可变对象陷阱:即使深拷贝,包含的可变子对象仍可修改
  • 大型对象谨慎深拷贝:考虑性能影响,必要时寻找替代方案
  • 自定义类实现拷贝方法:当需要特殊拷贝逻辑时
  • 文档记录拷贝行为:特别是当编写库或框架时

九、常见问题解答

Q1: 为什么list()构造函数创建的是浅拷贝?

A: 因为list()只复制顶层元素,对于可变子元素仍然共享引用。这是Python设计上的权衡,兼顾性能和常用场景需求。

Q2: 如何判断两个对象是否共享子对象?

A: 可以使用is操作符检查子对象引用是否相同:

a = [1, [2, 3]]
b = a.copy()
print(a[1] is b[1])  # True - 共享嵌套列表

Q3: 为什么深拷贝比浅拷贝慢这么多?

A: 深拷贝需要递归遍历整个对象图,为每个可变子对象创建新实例,而浅拷贝只需创建顶层对象并复制引用。

通过理解这些核心概念和实践技巧,您将能够更准确地控制Python中的对象复制行为,避免常见的编程陷阱,写出更健壮、高效的代码。

到此这篇关于一文带你深入了解Python中的深拷贝与浅拷贝数据的文章就介绍到这了,更多相关Python深拷贝与浅拷贝内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python自动创建Markdown表格使用实例探究

    Python自动创建Markdown表格使用实例探究

    Markdown表格是文档中整理和展示数据的重要方式之一,然而,手动编写大型表格可能会费时且容易出错,本文将介绍如何使用Python自动创建Markdown表格,通过示例代码详细展示各种场景下的创建方法,提高表格生成的效率
    2024-01-01
  • Python爬虫HTPP请求方法有哪些

    Python爬虫HTPP请求方法有哪些

    在本篇内容里小编给大家整理的是关于Python爬虫HTPP请求方法以及相关知识点,需要的朋友们可以参考下。
    2020-06-06
  • Python装饰器原理与简单用法实例分析

    Python装饰器原理与简单用法实例分析

    这篇文章主要介绍了Python装饰器原理与简单用法,结合实例形式分析了Python装饰器的概念、原理、使用方法及相关注意事项,需要的朋友可以参考下
    2018-04-04
  • Python 串口通信的实现

    Python 串口通信的实现

    这篇文章主要介绍了Python的串口通信的相关资料,帮助大家更好的理解和学习python,感兴趣的朋友可以了解下
    2020-09-09
  • 用python打印1~20的整数实例讲解

    用python打印1~20的整数实例讲解

    在本篇内容中小编给大家分享了关于python打印1~20的整数的具体步骤以及实例方法,需要的朋友们参考下。
    2019-07-07
  • 使用python绘制常用的图表

    使用python绘制常用的图表

    本文给大家介绍的是如何使用Python根据Excel表格数据绘制不同的图表的方法,非常的详细,有相同需求的小伙伴可以参考下
    2016-08-08
  • Python实现轻松读取大文件的技巧揭秘

    Python实现轻松读取大文件的技巧揭秘

    Python提供了多种方法来读取文件内容,其中包括read()、readline()和readlines()三个常用的函数,本文将深入探讨这三个函数的使用方法,需要的可以参考一下
    2023-08-08
  • 使用 Python 实现微信群友统计器的思路详解

    使用 Python 实现微信群友统计器的思路详解

    这篇文章主要介绍了使用 Python 实现微信群友统计器的思路详解,需要的朋友可以参考下
    2018-09-09
  • Python数据可视化之画图

    Python数据可视化之画图

    今天小编就为大家分享一篇关于Python数据可视化之画图,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • python登录pop3邮件服务器接收邮件的方法

    python登录pop3邮件服务器接收邮件的方法

    这篇文章主要介绍了python登录pop3邮件服务器接收邮件的方法,涉及Python操作邮件的相关技巧,需要的朋友可以参考下
    2015-04-04

最新评论