Python浅拷贝与深拷贝:核心区别与应用场景

Python中浅拷贝 VS 深拷贝


文章目录

  • Python中浅拷贝 VS 深拷贝
    • 问题案例
    • 一、知识点详解
      • 1.1 基本定义
      • 1.2 核心区别
      • 1.3 实现方式对比
      • 1.4 应用场景
      • 1.5 注意事项
    • 二、说明示例
    • 三、总结说明
    • 四、扩展知识
      • 4.1 可变对象与不可变对象


问题案例

在说明Python中深拷贝与浅拷贝的知识点之前,咱们先来看一下昨天的知识点考察题

a = [1, [2, 3]]
b = a.copy()
a[1].append(4)
print(b)

以上代码输出结果是什么?()
A. [1, [2, 3]]
B. [1, [2, 3, 4]]
C. [1, 2, 3, 4]
D. [1, 4, [2, 3]]

这道题的正确答案是:B

咱们来看下这段代码,
第一行:定义了一个变量a,a的值是是一个嵌套列表
第二行:拷贝a的值,然后赋值给b,此时b = [1, [2, 3]]
第三行:对嵌套列表a的索引为1的元素(即[2,3])进行末尾追加元素操作,此时a = [1, [2, 3, 4]]
第四行:打印输出b

那么,你是否会有疑惑:明明我是对a进行的追加元素操作,为什么b的值也同步改变了?
这其实就是Python中的浅拷贝陷阱,学完今天的知识点后,你就会明白。
好的,下面让我们进入今天的知识点学习环节


一、知识点详解

在 Python 中,浅拷贝深拷贝是两种对象复制机制,其核心区别在于对嵌套对象的处理方式。以下是详细解析:

1.1 基本定义

  • 浅拷贝
    仅复制对象的最外层结构,嵌套的可变对象(如列表、字典)仍与原对象共享引用。修改浅拷贝中的嵌套对象会影响原对象。
    实现方式copy.copy()、列表切片 list[:]、字典的 dict.copy()object.copy() 方法等 。

    copy.copy() 方法
    属于 Python 内置模块方法,用于对象的浅拷贝,使用前需导入模块:

    import copy
    new_obj = copy.copy(old_obj)
    
  • 深拷贝
    通过递归复制对象及其所有嵌套对象,生成完全独立的副本。修改深拷贝的嵌套对象不会影响原对象。
    实现方式copy.deepcopy()

    copy.deepcopy() 方法
    属于 Python 内置模块方法,用于对象的深拷贝,使用前需导入模块:

    import copy
    new_obj = copy.deepcopy(old_obj)
    

1.2 核心区别

特性 浅拷贝 深拷贝
复制层级 仅复制第一层对象 递归复制所有层级的嵌套对象
内存引用 嵌套可变对象共享原对象内存地址 所有层级对象均为独立内存空间
修改影响 修改嵌套对象会影响原对象 完全隔离,互不影响
适用场景 简单对象或不可变嵌套结构 复杂嵌套对象需完全独立

1.3 实现方式对比

浅拷贝方法

  • copy.copy():通用的浅拷贝函数
  • 切片操作:如 new_list = old_list[:]
  • 容器方法:如 dict.copy()list.copy()
  • object.copy():取决于具体的对象类型

深拷贝方法

  • copy.deepcopy():唯一支持递归复制的函数,处理所有嵌套对象

1.4 应用场景

浅拷贝适用场景

  • 对象仅包含不可变类型(如字符串、元组)
  • 需要快速复制且不关心嵌套对象共享时(如临时数据操作)

深拷贝适用场景

  • 数据需完全独立(如多线程 / 多进程环境中的数据安全)
  • 嵌套结构复杂且可能被多次修改(如配置模板的复用)

1.5 注意事项

不可变对象:若嵌套对象不可变(如元组),浅拷贝和深拷贝效果相同
易混淆知识
a = b是浅拷贝?不是a = b属于直接赋值(对象的引用),而非浅拷贝


二、说明示例

# 把下面的代码复制进Python环境中运行,查看输出结果,加深理解
import copy

print("\nPython 浅拷贝和深拷贝的区别\n")

print(f"{' 浅拷贝 ':=^50}")
print("浅拷贝:只复制对象的引用,原对象和拷贝对象共享同一内存地址\n")
# 浅拷贝
list1 = [1, 2, 3, [4, 5]]
print(f"原列表:{list1}")  
list2 = copy.copy(list1)  # 使用 copy.copy() 进行浅拷贝

# 浅拷贝列表:浅拷贝只复制了第一层元素,第二层元素仍然指向同一对象
list2[0] = 10 # 修改第一层元素
print(f"{'修改第一层元素后':-^30}")
print(f"原列表:{list1}")  # 输出: [1, 2, 3, [4, 5]]
print(f"浅拷贝列表:{list2}")  # 输出: [10, 2, 3, [4, 5]]

list2[3][0] = 20  # 修改第二层元素
print(f"{'修改第二层元素后':-^30}")
print(f"原列表:{list1}")  # 输出: [1, 2, 3, [20, 5]]
print(f"浅拷贝列表:{list2}")  # 输出: [10, 2, 3, [20, 5]]


# 深拷贝
print(f"\n{' 深拷贝 ':=^50}")
print("深拷贝:复制对象及其所有子对象,原对象和拷贝对象不共享内存地址\n")
list1 = [1, 2, 3, [4, 5]]
print(f"原列表:{list1}")
list2 = copy.deepcopy(list1)  # 使用 copy.deepcopy() 进行深拷贝

# 深拷贝列表:深拷贝会复制所有层级的元素
list2[0] = 10 # 修改第一层元素
print(f"{'修改第一层元素后':-^30}")
print(f"原列表:{list1}")  # 输出: [1, 2, 3, [4, 5]]
print(f"深拷贝列表:{list2}")  # 输出: [10, 2, 3, [4, 5]]

list2[3][0] = 20 # 修改第二层元素
print(f"{'修改第二层元素后':-^30}")
print(f"原列表:{list1}")  # 输出: [1, 2, 3, [4, 5]]
print(f"深拷贝列表:{list2}")  # 输出: [10, 2, 3, [20, 5]]

三、总结说明

选择浅拷贝或深拷贝需根据数据结构和应用需求决定。
若需完全独立且不介意性能损耗,优先使用深拷贝;
若数据结构简单或需高效复制,浅拷贝更合适。
理解两者的区别能有效避免因共享引用导致的意外错误 。


四、扩展知识

4.1 可变对象与不可变对象

在Python中“一切皆为对象”(面向对象编程概念,先行了解)是核心设计理念,意味着所有数据类型、函数、类、模块甚至代码本身都是对象。这种设计赋予了Python极高的灵活性和动态性。作为对象,我们又可以将其细化分为:可变对象和不可变对象。

不可变对象

  • 定义:创建后内容不可修改,任何修改操作都会创建新对象。
  • 常见类型int(整数)、float(浮点数)、bool(布尔值)、str(字符串)、tuple(元组)、frozenset(不可变集合)等

可变对象

  • 定义:创建后可以直接修改内容,内存地址不变
  • 常见类型list(列表)、dict(字典)、set(集合) 等

核心区别对比

特性 不可变数据类型 可变数据类型
修改操作 生成新对象 直接修改原对象
内存效率 频繁修改时会占用更多内存 适合频繁修改的场景
安全特性 线程安全,数据不易被意外修改 需注意共享引用的副作用
典型示例 int, str, tuple list, dict, set

可变性验证
id() 函数返回对象的唯一标识符,常用于验证对象身份:

# 不可变类型(字符串)
s = "abc"
print(f"原地址: {id(s)}")  # 输出地址 A
s += "def"                # 生成新对象
print(f"修改后地址: {id(s)}")  # 输出新地址 B(与原地址不同)

# 可变类型(列表)
lst = [1, 2, 3]
print(f"原地址: {id(lst)}")  # 输出地址 C
lst.append(4)               # 直接修改原对象
print(f"修改后地址: {id(lst)}")  # 地址仍是 C

通过理解可变与不可变对象的特性,可以避免常见错误(如默认参数陷阱),并优化内存使用和代码安全性。


关注公众号「安于欣」获取更多Python技巧

你可能感兴趣的:(Python学习笔记,python,开发语言)