10.python-深拷贝和浅拷贝
python-深拷贝和浅拷贝
变量: 存储对象的引用
对象:会分配一块内存空间,存储实际数据
引用:变量指向对象,理解为指针
变量存储在栈内存,对象存储在堆内存。
Python数据类型分为可变数据类型和不可变数据类型。
- 可变数据类型包括:List(列表)、Dictionary(字典)、Set(集合)
- 不可变数据类型包括:String(字符串)、Number(数字)、Tuple(元组)
赋值
Python 的赋值语句并不是创建一个新对象,只是创建了一个共享原始对象引用的新变量
不可变对象的赋值
a = 1
b = a
print(a, b)
a += 2
print(a, b)
print("a id:", id(a))
print("b id:", id(b))
# 输出结果
1 1
3 1
a id: 4564097808
b id: 4564097776
- 修改变量 a 的值,不会同步修改变量 b 的值
- 因为赋值操作 a += 2 后,变量 a 存储的对象引用已经改变了
可变对象的赋值
当其中一个内数据改变后,另一个也改变了
a = [1, 2, 3]
b = a
print(a, b)
a[1] = 22
print(a, b)
print("a id:", id(a))
print("b id:", id(b))
# 输出结果
[1, 2, 3] [1, 2, 3]
[1, 22, 3] [1, 22, 3]
a id: 4567683136
b id: 4567683136
- 修改 a 变量的值,会同步修改变量 b 的值,因为变量 a、b 指向的对象是可变对象,所以它们保存的对象引用都是一样的
浅拷贝
浅拷贝会创建一个新对象,该新对象存储原始元素的引用
- (1)不拷贝子对象的内容,只拷贝子对象的引用
- (2)可以使用Python内置函数copy()
浅拷贝的三种形式
切片-可变序列
lst = [1,2,[3,4]]
list1=[1,2,3]
list2=list1[:]
切片是属于浅拷贝,修改数据时,会对原先的造成污染
对于不可变数据类型(元组、字符串):
元组 使用 tuple()和切片操作符:
字符串 使用 str()和切片操作符:
都不是浅拷贝,没有开辟新内存来存储原对象对子对象的引用
列表的切片是浅拷贝(创建新的内存空间),元组的切片是赋值(不会创建新的内存空间)
切片操作符不能用于字典和集合完成浅拷贝
工厂函数-构造器
使用数据类型本身构造器
lst1 = list(lst)
#列表list
list1=[1,2,3]
list2=list(list1)
#set
set1=([1,2,3])
set2=set(set1)
#字典dict
dict1={1:[1,'w'],2:33}
dict2=dict(dict1)
copy.copy()
list1=[1,2,3]
list2=list1.copy()
import copy #需要先导入copy模块
list1=[1,2,3]
list2=copy.copy(list1)
浅拷贝的原对象的数据改变
浅拷贝在拷贝时,只会copy一层,在内存中开辟一个空间,存放这个copy的列表。更深的层次并没有copy,即第二层用的都是同一个内存。
list1=[(1,2),3,[4,5]]
list2=list1.copy()
list1.append(31)
print(list1)
print(list2)
[(1, 2), 3, [4, 5], 31]
[(1, 2), 3, [4, 5]]
list1=[(1,2),3,[4,5]]
list2=list1.copy()
list2.append(31)
print(list1)
print(list2)
[(1, 2), 3, [4, 5]]
[(1, 2), 3, [4, 5], 31]
list1=[(1,2),3,[4,5]]
list2=list1.copy()
list1[0]+=(31,31)
print(list1)
print(list2)
[(1, 2,31,31), 3, [4, 5]]
[(1, 2), 3, [4, 5]]
list1=[(1,2),3,[4,5]]
list2=list1.copy()
list1[2]+=[31,31]
print(list1)
print(list2)
[(1, 2), 3, [4, 5,31,31]]
[(1, 2), 3, [4, 5,31,31]]
深拷贝
- (1)会连子对象的内存全部拷贝一份,对子对象的修改不会影响源对象
- (2)可以使用Python内置函数deepcopy()
import copy
list_01 = ["11","22"]
list_02 = ["33",list_01]
list_03 = copy.deepcopy(list_02)
print(id(list_01))
print(id(list_02))
print(id(list_03))
print(id(list_02[1]))
print(id(list_03[1]))
list_01.append("44")
print(list_01)
print(list_02)
print(list_03)
['33', ['11', '22', '44']]
['33', ['11', '22']]
通过深拷贝即使嵌套的列表具有更深的层次,也不会产生任何影响,因为深拷贝拷贝出来的对象根本就是一个全新的对象,不再与原来的对象有任何的关联。
总结
- 浅拷贝花费时间少,占用内存少,只拷贝顶层数据,拷贝效率高。
- 对不可变对象拷贝时,浅拷贝和深拷贝的作用是一致的,不开辟新空间,相当于赋值操作。
- 可变对象浅拷贝时,只拷贝第一层中的引用,如果元素是可变对象,并且被修改,那么拷贝的对象也会发生变化。
- 可变对象深拷贝时,会逐层进行拷贝,遇到可变类型,就开辟一块内存复制下来,不论哪一层元素被修改,拷贝的对象都不会发生改变。
不可变对象可以直接赋值,可变对象只有一层时,建议浅拷贝,如果可变对象是多层,建议深拷贝。