深浅拷贝
2026/7/3大约 7 分钟
1.深浅拷贝概述
1.1 三句话总结
第一句话: 所谓的深浅拷贝分别指的是:
- 浅拷贝:
copy模块的copy()函数 - 深拷贝:
copy模块的deepcopy()函数
第二句话: 深拷贝拷贝的多(深),浅拷贝拷贝的少(浅)
第三句话: 深浅拷贝主要是针对可变类型来讲的:
- 深拷贝拷贝所有层
- 浅拷贝只拷贝第一层
- 如果是针对不可变类型,则用法和普通赋值一样,并无区别
1.2 可变类型与不可变类型
| 类型 | 特点 | 示例 |
|---|---|---|
| 可变类型 | 能在不改变地址值的情况下修改内容 | 列表、字典、集合 |
| 不可变类型 | 一旦创建就不能改变 | 字符串、元组、整数、浮点型 |
1.3 面试重点
面试只会考深浅拷贝拷贝可变类型,因为不可变类型太简单了,用法和普通赋值一样。
2.普通赋值
2.1 不可变类型
a = 10
b = a
print("id(a)-->", id(a))
print("id(b)-->", id(b))
print("id(10)-->", id(10))执行结果:
id(a)--> 140712345678976
id(b)--> 140712345678976
id(10)--> 140712345678976说明: a、b、10 的地址相同,都是同一个对象。
2.2 可变类型
a = [1, 2, 3]
b = [11, 22, 33]
c = [a, b]
d = c
print("id(c)-->", id(c))
print("id(d)-->", id(d))执行结果:
id(c)--> 2541715462592
id(d)--> 2541715462592说明: c 和 d 的地址相同,普通赋值只是复制了引用,没有创建新对象。
2.3 普通赋值图解
普通赋值:d = c
┌─────────────────────────────────────┐
│ c (0x03) │
│ ┌─────────────────────────────┐ │
│ │ 0x01 (a) 0x02 (b) │ │
│ └─────────────────────────────┘ │
│ ↑ │
│ d (0x03) ─┘ (指向同一块空间) │
└─────────────────────────────────────┘3.浅拷贝操作可变类型
3.1 代码实现
import copy
def demo_02():
a = [1, 2, 3]
b = [11, 22, 33]
c = [6, 7, a, b]
d = copy.copy(c)
print("id(c)-->", id(c))
print("id(d)-->", id(d))
print("id(c)和id(d)值不一样,说明浅拷贝创建了新的外层容器")
print(id(c[2]))
print(id(a))
print("id(c[2])和id(a)值一样,说明浅拷贝只复制了内层对象的引用")
a[2] = 22
print("c-->", c)
print("d-->", d)执行结果:
id(c)--> 2541715462592
id(d)--> 2541715462752
id(c)和id(d)值不一样,说明浅拷贝创建了新的外层容器
2541715462208
2541715462208
id(c[2])和id(a)值一样,说明浅拷贝只复制了内层对象的引用
c--> [6, 7, [1, 2, 22], [11, 22, 33]]
d--> [6, 7, [1, 2, 22], [11, 22, 33]]3.2 浅拷贝图解
浅拷贝:d = copy.copy(c)
┌─────────────────────────────────────┐
│ c (0x03) │
│ ┌─────────────────────────────┐ │
│ │ 6, 7, 0x01(a), 0x02(b) │ │
│ └─────────────────────────────┘ │
│ ↑ ↑ │
│ d (0x04) ─┘ └─ 共用第二层 │
│ ┌─────────────────────────────┐ │
│ │ 6, 7, 0x01(a), 0x02(b) │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘3.3 关键说明
浅拷贝特点
- 第一层(最外面一层)是新复制的,地址不同
- 第二层及更深层次是引用,地址相同
- 修改第二层的数据,
c和d都会受影响
4.浅拷贝操作不可变类型
4.1 代码实现
def demo_03():
a = (1, 2, 3)
b = (11, 22, 33)
c = (6, 7, a, b)
d = copy.copy(c)
print("id(c)-->", id(c))
print("id(d)-->", id(d))执行结果:
id(c)--> 2541715462592
id(d)--> 25417154625924.2 关键说明
不可变类型的浅拷贝
浅拷贝操作不可变类型,用法和普通赋值一样,不会给拷贝的对象开辟新的内存空间,而只是拷贝了这个对象的引用。
5.深拷贝操作可变类型
5.1 代码实现
def demo_04():
a = [1, 2, 3]
b = [11, 22, 33]
c = [6, 7, a, b]
d = copy.deepcopy(c)
print("id(c)-->", id(c))
print("id(d)-->", id(d))
a[1] = 100
b[1] = 800
print(f"c:{c}")
print(f"d:{d}")执行结果:
id(c)--> 2541715462592
id(d)--> 2541715462752
c:[6, 7, [1, 100, 3], [11, 800, 33]]
d:[6, 7, [1, 2, 3], [11, 22, 33]]5.2 深拷贝图解
深拷贝:d = copy.deepcopy(c)
┌─────────────────────────────────────┐
│ c (0x03) │
│ ┌─────────────────────────────┐ │
│ │ 6, 7, 0x01(a), 0x02(b) │ │
│ └─────────────────────────────┘ │
│ │
│ d (0x04) │
│ ┌─────────────────────────────┐ │
│ │ 6, 7, 0x05(新a), 0x06(新b) │ │
│ └─────────────────────────────┘ │
│ │
│ a (0x01) [1, 2, 3] │
│ 新a (0x05) [1, 2, 3] ← 完全独立 │
│ │
│ b (0x02) [11, 22, 33] │
│ 新b (0x06) [11, 22, 33] ← 完全独立 │
└─────────────────────────────────────┘5.3 关键说明
深拷贝特点
- 深拷贝会拷贝所有层,包括嵌套的可变类型
- 每一层都是新复制的,地址不同
- 修改
c中的数据,d不受影响 - 能保证数据的安全
6.深拷贝操作不可变类型
6.1 代码实现
def demo_05():
a = (1, 2, 3)
b = (11, 22, 33)
c = (6, 7, a, b, {"a": 3, "b": 4})
d = copy.deepcopy(c)
print("id(c)-->", id(c))
print("id(d)-->", id(d))
c[4]['a'] = 11
print(c)
print(d)执行结果:
id(c)--> 2541715462592
id(d)--> 2541715462752
(6, 7, (1, 2, 3), (11, 22, 33), {'a': 11, 'b': 4})
(6, 7, (1, 2, 3), (11, 22, 33), {'a': 11, 'b': 4})6.2 关键说明
不可变类型的深拷贝
深拷贝操作不可变类型,若为不可变类型直接引用,不开辟新的空间。但如果有嵌套的可变类型(如字典),则会开辟新空间。
7.深浅拷贝对比总结
7.1 三种操作对比
| 操作 | 第一层 | 第二层及更深层次 | 地址 |
|---|---|---|---|
| 普通赋值 | 引用 | 引用 | 相同 |
| 浅拷贝 | 新复制 | 引用 | 第一层不同,深层相同 |
| 深拷贝 | 新复制 | 新复制(可变类型) | 都不同 |
7.2 可变类型 vs 不可变类型
| 操作 | 可变类型 | 不可变类型 |
|---|---|---|
| 浅拷贝 | 只拷贝第一层 | 用法和普通赋值一样 |
| 深拷贝 | 拷贝所有层 | 用法和普通赋值一样 |
7.3 面试重点
- 深浅拷贝主要是针对可变类型来讲的
- 深拷贝拷贝所有层,浅拷贝只拷贝第一层
- 如果是针对不可变类型,则用法和普通赋值一样,并无区别
- 面试只会考深浅拷贝拷贝可变类型
8.完整代码
"""
1.所谓的申请拷贝分别指的是:
浅拷贝:copy模块的copy()函数
深拷贝:copy模块的deepcopy()函数
2.深拷贝拷贝的多,浅拷贝拷贝的少
3.深浅拷贝主要是针对于可变类型来讲的,深拷贝拷贝所有层(可变),浅拷贝只拷贝第一层(可变)
如果是针对于不可变类型,则用法和普通赋值一样,并无区别
"""
import copy
# python的赋值操作属于引用赋值(b是a的别名,形参是实参的别名)
def demo_01():
# 普通赋值之不可变类型
a = 10
b = a
print("id(a)-->", id(a))
print("id(b)-->", id(b))
print("id(10)-->", id(10))
# 普通赋值之可变类型
a = [1, 2, 3]
b = [11, 22, 33]
c = [a, b]
d = c
print("id(c)-->", id(c))
print("id(d)-->", id(d))
# 浅拷贝可变类型:只拷贝第一层数据,深层次数据不拷贝
def demo_02():
a = [1, 2, 3]
b = [11, 22, 33]
c = [6, 7, a, b]
d = copy.copy(c)
print("id(c)-->", id(c))
print("id(d)-->", id(d))
print("id(c)和id(d)值不一样明说明浅拷贝第一层(最外面一层的数据)")
print(id(c[2]))
print(id(a))
print("id(c[2])和id(a)值一样,说明浅拷贝第二层的数据")
a[2] = 22
print("c-->", c)
print("d-->", d)
# 浅拷贝不可变类型:不会给拷贝的对象d开辟新的内存空间,而只是拷贝了这个对象的引用
def demo_03():
a = (1, 2, 3)
b = (11, 22, 33)
c = (6, 7, a, b)
d = copy.copy(c)
print("id(c)-->", id(c))
print("id(d)-->", id(d))
# 深拷贝可变类型,若为可变类型则开辟新的空间,所有层都会深拷贝
# 作用:能保证数据的安全
def demo_04():
a = [1, 2, 3]
b = [11, 22, 33]
c = [6, 7, a, b]
d = copy.deepcopy(c)
print("id(c)-->", id(c))
print("id(d)-->", id(d))
a[1] = 100
b[1] = 800
print(f"c:{c}")
print(f"d:{d}")
# 深拷贝不可变类型:若为不可变类型直接就引用了,不开辟新的空间
def demo_05():
a = (1, 2, 3)
b = (11, 22, 33)
c = (6, 7, a, b, {"a": 3, "b": 4})
d = copy.deepcopy(c)
print("id(c)-->", id(c))
print("id(d)-->", id(d))
c[4]['a'] = 11
print(c)
print(d)
if __name__ == '__main__':
demo_05()