赋值与深浅拷贝
文章目录
- 1 可变对象和不可变对象
- 2 赋值
- 3 浅拷贝
- 4 深拷贝
- 5 总结区分
1 可变对象和不可变对象
可变对象和不可变对象的划分依据:在不改变地址的前提下,元素值能否修改。
- 可变对象:该对象所指内存的值可以被改变
- 列表
list
- 集合
set
- 字典
dict
- 自定义对象
- 列表
- 不可变对象:该对象所指向的内存中的值不能被改变,一旦创建就不可修改,想要修改对象的值,需要复制一份后再改变,会返回一个新地址
- 数字
int
float
- 字符串
str
- 元组
tuple
- 布尔
bool
- 数字
可以通过id()
查看对象的地址。
# 不可变对象:改变元素值本质是创建了一个新的对象,指向了新的对象
a = 10
print(id(a)) # 1537202913808
a = 20
print(id(a)) # 1537202914128,内容修改,地址也变了# 可变对象:
ls = [1, 2, 4]
print(id(ls)) # 1995083673536
ls[1] = 6
print(id(ls)) # 1995083673536,内容修改,地址不变
2 赋值
与C++不同,Python的赋值=
属于引用赋值,只是了创建对象的另一个引用(别名),新旧变量指向同一个内存对象,通过任一引用修改对象(需要可变),其他引用都会发生变化。
# 引用赋值,a和b都指向同一内存空间
a = 10
b = a
print(id(a)) # 1437435953680
print(id(b)) # 1437435953680
print(id(10)) # 1437435953680# c和d也指向同一内存空间
c = [1, 3, 5]
d = c
print(id(c)) # 2028769177536
print(id(d)) # 2028769177536
Python解释器在执行a = 10
赋值时做以下操作:
- 创建变量 a
- 创建一个对象(分配一块内存),来存储值10
- 将变量与对象,通过指针连接起来,从变量到对象的连接称之为引用(变量引用对象)
3 浅拷贝
浅拷贝和深拷贝使用需要导入import copy
模块,它们的区别是拷贝层级不同,都可以操作可变对象和不可变对象,但是一般不会操作不可变对象。
不论是深拷贝还是浅拷贝,针对不可变类型的拷贝,都不会开辟新的空间,而是拷贝对象的引用。
import copy a = (1, 2, 3) b = copy.copy(a) c = copy.deepcopy(a) print(id(a)) # 1553475958912 print(id(b)) # 1553475958912 print(id(c)) # 1553475958912
浅拷贝copy.copy()
会创建新对象,复制最外层的数据,但内部容器仍然是引用。
- 外层独立:外层修改互不影响
- 共享内层:嵌套对象的修改会影响所有拷贝
import copy
a = [1, 2, 3, [4, 5, 6]]
b = copy.copy(a) # 实际拷贝了 [1, 2, 3, 地址]# 外层在新的对象中
print(id(a)) # 2785582893824
print(id(b)) # 2785582894016# 内层还是原来的引用
print(id(a[3])) # 2785582896960
print(id(b[3])) # 2785582896960a[0] = 66
a[3][0] = 66print(a) # [66, 2, 3, [66, 5, 6]]
print(b) # [1, 2, 3, [66, 5, 6]],外层独立,内层仍然是旧引用
在最外层只暴露了深层的引用,浅拷贝只会拷贝这个地址,而不会拷贝深层数据。
4 深拷贝
深拷贝copy.deepcopy()
会创建新的对象,并且完全独立的,会递归复制所有嵌套对象。
- 完全隔离:任何修改都不会影响其他拷贝
- 内存开销:需要更多内存,因为创建所有对象的副本
import copy
a = [1, 2, 3, [4, 5, 6]]
b = copy.deepcopy(a) # 外层是独立的
print(id(a)) # 1823162354432
print(id(b)) # 1823162354624# 内层也是独立的
print(id(a[3])) # 1823162357568
print(id(b[3])) # 1823162357952a[0] = 66
a[3][0] = 66 print(a) # [66, 2, 3, [66, 5, 6]]
print(b) # [1, 2, 3, [4, 5, 6]],完全独立,不受影响
5 总结区分
普通赋值和浅拷贝:
浅拷贝和深拷贝:
- 浅拷贝只拷贝第一层中的数据并开辟空间存储
- 深拷贝拷贝所有的数据并开辟对应的空间存储
拷贝不可变类型:
深浅拷贝对于不可变类型来说,非常类似普通的赋值,只是复制了引用。