python的赋值,深拷贝和浅拷贝的区别
一、核心概念总览
1. 赋值操作 =
只是给同一个内存地址起别名,没有创建新对象
多个变量完全指向同一个对象,任意一个变量原地修改,所有变量都会跟着变。
2. 浅拷贝 copy()
创建一个新的外层容器对象,容器里的元素依然是原对象中元素的引用;
- 不可变元素:直接引用
- 可变元素:依然共享同一个内存地址
只拷贝第一层,嵌套的可变对象不拷贝。
3. 深拷贝 deepcopy()
递归拷贝所有层级的对象,不管嵌套多少层可变对象,全部创建新内存,完全独立,修改互不影响。
二、1. 赋值 =
python
运行
a = [1, 2, [3, 4]]
b = a
print(id(a), id(b)) # 内存地址完全相同
b[2].append(5)
print(a) # [1, 2, [3, 4, 5]] a跟着变
特点:
- 不产生新对象,只是引用指向同一个地址
- 不管一维还是多维,修改任意一处,全部受影响
三、2. 浅拷贝 copy
三种浅拷贝方式:
list.copy()copy.copy()- 切片
lst[:]
python
运行
import copy
a = [1, 2, [3, 4]]
b = copy.copy(a)
print(id(a), id(b)) # 外层列表地址不同(新对象)
print(id(a[2]), id(b[2])) # 内层嵌套列表地址相同
# 修改外层不可变元素
b[0] = 10
print(a) # [1,2,[3,4]] 原数组不受影响
# 修改内层可变嵌套对象
b[2].append(5)
print(a) # [1, 2, [3, 4, 5]] 原数组被修改
浅拷贝总结:
- 第一层:新对象,相互独立
- 嵌套层(第二层及以后):共用引用,修改会互相影响
四、3. 深拷贝 deepcopy
python
运行
import copy
a = [1, 2, [3, 4]]
b = copy.deepcopy(a)
# 外层、内层所有对象地址全部不一样
print(id(a), id(b))
print(id(a[2]), id(b[2]))
b[2].append(5)
print(a) # [1, 2, [3, 4]] 完全不受影响
特点:递归拷贝每一层,所有对象全部新建,完全隔离。
五、可变 & 不可变对象的特殊情况
场景 1:纯一维不可变对象(元组、字符串、数字)
浅拷贝、深拷贝都不会创建新对象,本质等价于赋值。
python
运行
import copy
t = (1, 2, 3)
t1 = copy.copy(t)
t2 = copy.deepcopy(t)
print(id(t), id(t1), id(t2)) # 地址全部一样
场景 2:元组里嵌套可变对象
浅拷贝依然复用原元组;深拷贝会创建新元组 + 嵌套新列表。
六、三者对比表
表格
| 方式 | 是否新建外层对象 | 嵌套可变对象是否新建 | 修改原对象是否影响副本 |
|---|---|---|---|
赋值= |
❌ 否 | ❌ 否 | 全部相互影响 |
浅拷贝copy |
✅ 是 | ❌ 否 | 改第一层互不影响,改嵌套会影响 |
深拷贝deepcopy |
✅ 是 | ✅ 递归全部新建 | 完全互不影响 |
七、高频面试笔试题
题 1:下面输出结果?
python
运行
import copy
l1 = [1, [2, 3]]
l2 = copy.copy(l1)
l2[1].append(4)
print(l1)
答案:[1, [2, 3, 4]]
解析:浅拷贝只拷贝第一层,内层列表共享引用。
题 2:深拷贝优缺点
优点:数据完全隔离,修改互不干扰;
缺点:递归拷贝,如果数据量极大,耗时、占用内存高。
题 3:什么时候用浅拷贝,什么时候深拷贝?
- 一维列表 / 字典,没有嵌套可变对象:用浅拷贝足够;
- 存在多层嵌套列表、字典等可变类型:必须深拷贝,防止数据污染。
题 4:为什么元组浅拷贝不生成新对象?
因为元组是不可变类型,无法原地修改,解释器做了优化,直接返回原对象引用节省内存。
八、易错总结
- 赋值:别名,同一块内存;
- 浅拷贝:只拷贝第一层,嵌套可变对象共用;
- 深拷贝:所有层级全部新建,完全独立;
- 不可变类型浅 / 深拷贝都不会产生新对象;
- 字典浅拷贝:
dict.copy()、copy.copy(dict)。
赋值、浅拷贝、深拷贝 5 道经典面试笔试题(含答案 + 详细解析)
题目 1
python
运行
import copy
a = [1, 2, [3, 4]]
b = a
b[0] = 10
b[2].append(5)
print(a)
输出结果
[10, 2, [3, 4, 5]]解析
b = a属于赋值引用,a、b 指向同一个列表对象,不管修改第一层还是内层嵌套元素,原列表都会被修改。题目 2
python
运行
import copy
a = [1, 2, [3, 4]]
b = copy.copy(a)
b[0] = 10
b[2].append(5)
print(a)
输出结果
[1, 2, [3, 4, 5]]解析
浅拷贝只创建外层新列表,第一层元素修改互不影响;内层嵌套的列表依旧是同一个引用,对内层列表原地追加数据,原数据会被改变。
题目 3
python
运行
import copy
a = [1, 2, [3, 4]]
b = copy.deepcopy(a)
b[0] = 10
b[2].append(5)
print(a)
输出结果
[1, 2, [3, 4]]解析
深拷贝会递归拷贝所有层级对象,外层、内层嵌套列表都会生成全新内存地址,两个列表完全隔离,修改 b 不会对 a 产生任何影响。
题目 4
python
运行
import copy
t = (1, 2, [3, 4])
t1 = copy.copy(t)
t1[2].append(99)
print(t)
输出结果
(1, 2, [3, 4, 99])解析
元组是不可变类型,浅拷贝不会创建新元组,t 和 t1 指向同一个元组;元组约束的是元素引用不能修改,内部嵌套的列表可变,可以原地追加元素,因此原元组数据发生变化。
拓展:如果用
deepcopy,会生成新元组,修改不会影响原对象。题目 5
python
运行
import copy
d1 = {"name":"test", "info":[10,20]}
d2 = d1.copy()
d2["name"] = "demo"
d2["info"].append(30)
print(d1)
输出结果
{'name': 'test', 'info': [10, 20, 30]}解析
字典
copy()属于浅拷贝,外层字典是新对象,修改字符串键值互不影响;嵌套列表共享引用,追加元素后原字典的列表同步变化。附加易错简答题 3 道
-
一维纯数字列表,浅拷贝之后修改,原列表会变吗?答:不会。一维没有嵌套可变对象,修改第一层会重新赋值绑定新地址,不会影响原列表。
-
字符串做浅拷贝会生成新对象吗?答:不会,字符串属于不可变类型,浅拷贝、深拷贝都直接返回原对象引用。
-
切片
lst[:]属于浅拷贝吗?答:属于,只拷贝第一层,嵌套可变对象依旧共享引用。
浙公网安备 33010602011771号