一、深拷贝和浅拷贝概念
- 浅拷贝:重新分配一块内存,创建一个新的对象,拷贝父对象,不会拷贝对象内部的子对象。
- 深拷贝:重新分配一块内存,创建一个新的对象,完全拷贝父对象和子对象,新对象和原对象没有任何关联。
1.1 简单来说:
- 浅拷贝只复制某个对象的引用,而不复制对象本身,新旧对象还是共享同一块内存。
- 深拷贝会创造一个一模一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对象。
1.2 深浅拷贝优缺点:
- 浅拷贝耗时短,占用内存空间少。
- 深拷贝耗时长,且占用内存空间。
二、浅拷贝
2.1 只有一层数据类型,且为可变数据类型,如列表、字典。
"""
只有一层数据类型,且为可变数据类型,
浅拷贝后会创建一个新的内存地址
"""
from copy import copy
l1 = [1, 2, 3, 4]
l2 = copy(l1)
print(id(l1)) # 34653376
print(id(l2)) # 35040256
l1.append(5)
l2.append(6)
print(l1) # [1, 2, 3, 4, 5]
print(l2) # [1, 2, 3, 4, 6]
2.2 只有一层数据类型,且为不可变数据类型
"""
只有一层数据类型,且为不可变数据类型,
浅拷贝后内存地址不变
"""
from copy import copy
l1 = "1234"
l2 = copy(l1)
print(id(l1)) # 38998256
print(id(l2)) # 38998256
2.3 有两层数据类型,外层为可变数据类型,内层包含可变数据类型元素
"""
有两层数据类型,外层为可变数据类型,内层包含可变数据类型
浅拷贝后外层地址改变,内层地址不变
"""
from copy import copy
l1 = [1, 2, 3, 4, [100, 200]]
l2 = copy(l1)
print(id(l1)) # 40283008
print(id(l2)) # 40440768
print(id(l1[4])) # 39306240
print(id(l2[4])) # 39306240
# 内层地址不变可以减少内存地址开销
l1[4].append(300)
print(l1) # [1, 2, 3, 4, [100, 200, 300]]
print(l2) # [1, 2, 3, 4, [100, 200, 300]]
# 使用浅拷贝,如果元素中存在可变类型数据,
# 内层的子列表不会发生拷贝,所以子列表发生变化,对应拷贝后的列表也发生变化
三、深拷贝
3.1 只有一层数据类型,且为可变数据类型,如列表、字典。
"""
只有一层数据类型,且为可变数据类型,
深拷贝后会创建一个新的内存地址
"""
from copy import deepcopy
l1 = [1, 2, 3, 4]
l2 = deepcopy(l1)
print(id(l1)) # 39371840
print(id(l2)) # 40086400
l1.append(5)
l2.append(6)
print(l1) # [1, 2, 3, 4, 5]
print(l2) # [1, 2, 3, 4, 6]
3.2 只有一层数据类型,且为不可变数据类型
"""
只有一层数据类型,且为不可变数据类型,
深拷贝后内存地址不变
"""
from copy import deepcopy
l1 = "1234"
l2 = deepcopy(l1)
print(id(l1)) # 34544432
print(id(l2)) # 34544432
3.3 有两层数据类型,外层为可变数据类型,内层包含可变数据类型元素
"""
有两层数据类型,外层为可变数据类型,内层包含可变数据类型
深拷贝后外层地址改变,内层地址改变
"""
from copy import deepcopy
l1 = [1, 2, 3, 4, [100, 200]]
l2 = deepcopy(l1)
print(id(l1)) # 40217536
print(id(l2)) # 40375296
print(id(l1[4])) # 39502976
print(id(l2[4])) # 39152832
# 深拷贝后外层地址改变
l1[4].append(300)
print(l1) # [1, 2, 3, 4, [100, 200, 300]]
print(l2) # [1, 2, 3, 4, [100, 200]]
# 使用深拷贝,如果元素中存在可变类型数据,
# 内层的子列表也发生拷贝,子列表变化,拷贝后的列表不会变化
3.4 有两层数据类型,外层为可变数据类型,内层包含不可变数据类型元素
"""
有两层数据类型,外层为可变数据类型,
内层不可变数据类型的元素,深拷贝不改变内层地址,
内层是可变数据类型的元素,深拷贝改变内层地址
"""
from copy import deepcopy
l1 = [1, 2, 3, [100, 200], "1121"]
l2 = deepcopy(l1)
# 外层是可变数据类型,深拷贝,外层地址改变
print(id(l1)) # 40152000
print(id(l2)) # 40301888
# 内层是可变数据类型的元素,深拷贝,该元素对应的内层地址发生改变
print(id(l1[3])) # 39437504
print(id(l2[3])) # 39438144
# 内层是不可变数据类型的元素,深拷贝,该元素对应的内层地址不变
print(id(l1[4])) # 39066416
print(id(l2[4])) # 39066416
四、深拷贝和浅拷贝总结
- 浅拷贝只有外层是可变数据类型时,才能改变外层地址。内层地址无论是否是可变数据类型,都不会改变内层地址。所以,浅拷贝不会拷贝子列表,子列表发生变化对应的拷贝后的数据也会发生变化。
- 深拷贝内层是可变数据类型时,内外层地址均发生改变。内层不可变数据类型的元素,深拷贝后对应元素处的内层地址不会改变。所以,深拷贝会拷贝子列表,子列表发生变化对应的拷贝后的数据不会发生变化。