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跟着变
 
特点:
  1. 不产生新对象,只是引用指向同一个地址
  2. 不管一维还是多维,修改任意一处,全部受影响

三、2. 浅拷贝 copy

三种浅拷贝方式:
  1. list.copy()
  2. copy.copy()
  3. 切片 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:什么时候用浅拷贝,什么时候深拷贝?

  1. 一维列表 / 字典,没有嵌套可变对象:用浅拷贝足够;
  2. 存在多层嵌套列表、字典等可变类型:必须深拷贝,防止数据污染。

题 4:为什么元组浅拷贝不生成新对象?

因为元组是不可变类型,无法原地修改,解释器做了优化,直接返回原对象引用节省内存。

八、易错总结

  1. 赋值:别名,同一块内存;
  2. 浅拷贝:只拷贝第一层,嵌套可变对象共用;
  3. 深拷贝:所有层级全部新建,完全独立;
  4. 不可变类型浅 / 深拷贝都不会产生新对象;
  5. 字典浅拷贝: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 道

  1. 一维纯数字列表,浅拷贝之后修改,原列表会变吗?
     
    答:不会。一维没有嵌套可变对象,修改第一层会重新赋值绑定新地址,不会影响原列表。
  2. 字符串做浅拷贝会生成新对象吗?
     
    答:不会,字符串属于不可变类型,浅拷贝、深拷贝都直接返回原对象引用。
  3. 切片lst[:]属于浅拷贝吗?
     
    答:属于,只拷贝第一层,嵌套可变对象依旧共享引用。
posted @ 2020-01-19 10:22  sjxm2017  阅读(128)  评论(0)    收藏  举报