感谢互联网上无私的知识贡献者——
本文参考了以下博客:
Python之美[从菜鸟到高手]–浅拷贝、深拷贝完全解读(copy源码分析)
欢迎大家指出本文中的不足,尽量及时更改,避免误导他人,Thanks♪(・ω・)ノ
环境
Python3.8
深、浅拷贝特点
对象类型 | 深拷贝返回值 | 浅拷贝返回值 |
---|---|---|
不可变 | 本身(不包括tuple) | 本身 |
可变 | 嵌套拷贝对象1 | 表层拷贝对象2 |
可能有点抽象,举个例子:
ele = [4]
list_0 = [1, 2, 3, ele]
# 形象点就是copy搬运个网站,里头有链接,也只是原原本本复制链接
# deepcopy在此之上,还会把链接指向的网站内容也复制了
# 例如:
copy_list_0 = [1, 2, 3, ele]
deepcopy_list_0 = [1, 2, 3, [4]]
注意:深拷贝能够递归拷贝,所以其会引发新的问题——tuple类组合内含有可变类型该如何处理?
结论:用tunple函数重新生成一个元组,并返回。
源码Document
以下为copy模块的说明截取:
The difference between shallow and deep copying is only relevant for
compound objects (objects that contain other objects, like lists or
class instances).
- A shallow copy constructs a new compound object and then (to the
extent possible) inserts *the same objects* into it that the
original contains.
- A deep copy constructs a new compound object and then, recursively,
inserts *copies* into it of the objects found in the original.
翻译:
浅复制和深复制的不同仅仅是对组合对象来说,所谓的组合对象就是包含了其它对象的对象,如列表,类实例。
–浅拷贝将构造一个新的组合对象,然后将所包含的对象直接插入到新的组合对象中
–深拷贝讲构造一个新的组合对象,然后递归的拷贝所包含的对象并插入到新的组合对象中
验证及源码说明
不可变类型
验证:对下列immutable进行深浅拷贝,对比源头id,验证均相同,得证。(输出略)
import copy
im_int = 0
im_bool = False
im_string = '0'
im_tuple = (0, 0)
if copy.copy(im_int) is im_int and \
copy.copy(im_bool) is im_bool and \
copy.copy(im_string) is im_string and \
copy.copy(im_tuple) is im_tuple and \
copy.deepcopy(im_int) is im_int and \
copy.deepcopy(im_bool) is im_bool and \
copy.deepcopy(im_string) is im_string and \
copy.deepcopy(im_tuple) is im_tuple:
print('immutable类型变量深浅备份仅返回本身')
else:
print('error')
浅拷贝源码
_copy_dispatch = d = {}
# 如果x为immutable类型,返回本身
def _copy_immutable(x):
return x
# 将包括immutable在内的,所有不可拷贝类型添加进字典_copy_dispatch(d)
for t in (type(None), int, float, bool, complex, str, tuple,
bytes, frozenset, type, range, slice, property,
types.BuiltinFunctionType, type(Ellipsis), type(NotImplemented),
types.FunctionType, weakref.ref):
d[t] = _copy_immutable
分析:copy模块先行设置不可拷贝类型的key为_copy_immutable方法,该方法返回x本身。
def copy(x):
"""Shallow copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
"""
# 获得x类型
cls = type(x)
# 获取该类型的处理方法,对于immutable类的type,均返回x本身
copier = _copy_dispatch.get(cls)
if copier:
return copier(x)
分析:在copy得到带拷贝目标x后,提取其type,并获取字典_copy_dispatch对应的处理方法,对于immutable类型则返回_copy_immutable方法——直接返回x本身。
深拷贝源码
# 同浅拷贝,获取对应方法并执行
copier = _deepcopy_dispatch.get(cls)
if copier is not None:
y = copier(x, memo)
def _deepcopy_atomic(x, memo):
return x
# d = _deepcopy_dispatch
d[type(None)] = _deepcopy_atomic
d[type(Ellipsis)] = _deepcopy_atomic
d[type(NotImplemented)] = _deepcopy_atomic
d[int] = _deepcopy_atomic
d[float] = _deepcopy_atomic
d[bool] = _deepcopy_atomic
d[complex] = _deepcopy_atomic
d[bytes] = _deepcopy_atomic
d[str] = _deepcopy_atomic
d[types.CodeType] = _deepcopy_atomic
d[type] = _deepcopy_atomic
d[types.BuiltinFunctionType] = _deepcopy_atomic
d[types.FunctionType] = _deepcopy_atomic
d[weakref.ref] = _deepcopy_atomic
d[property] = _deepcopy_atomic
分析:这里的d就是_deepcopy_dispatch。深拷贝对于除tuple外的immutable类型的处理相似,不做赘述。
以下重点看深拷贝对不可变类型tuple的处理:
源码及分析:
def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
# 使用deepcopy对x内的组合进行复制,得到y
y = [deepcopy(a, memo) for a in x]
# We're not going to put the tuple in the memo, but it's still important we
# check for it, in case the tuple contains recursive mutable structures.
try:
return memo[id(x)]
except KeyError:
pass
# 对比x、y中的元素,如果存在一个id不相等,则说明包含有可变类型
for k, j in zip(x, y):
if k is not j:
# 调用tuple重新生成元组
y = tuple(y)
break
else:
y = x
return y
d[tuple] = _deepcopy_tuple
分析:通过调用deepcopy递归赋值tuple内的元素,如果包含可变元素,deepcopy会用响应的方法进行复制,如_deepcopy_list方法,并通过tuple(y)重新生成一个元组并返回。如果不包含,则返回本身。
可变对象
验证:对下列可变类型进行copy,对比id,结果输出下列语句,得证。
ele = [4, 5]
mu_list = [1, 2, 3, ele]
mu_dic = {1: 1, 2: 2}
if copy.copy(mu_list) is not mu_list and \
copy.copy(mu_dic) is not mu_dic and \
copy.deepcopy(mu_list) is not mu_list and \
copy.deepcopy(mu_dic) is not mu_dic:
print('mutable类型变量深浅备份返回新的对象')
浅拷贝源码:
# 以下就直接添加对应模块的copy方法到copy模块所维护的d中
d[list] = list.copy
d[dict] = dict.copy
d[set] = set.copy
d[bytearray] = bytearray.copy
深拷贝源码:
def _deepcopy_list(x, memo, deepcopy=deepcopy):
y = []
memo[id(x)] = y
append = y.append
for a in x:
append(deepcopy(a, memo))
return y
d[list] = _deepcopy_list
def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
y = [deepcopy(a, memo) for a in x]
# We're not going to put the tuple in the memo, but it's still important we
# check for it, in case the tuple contains recursive mutable structures.
try:
return memo[id(x)]
except KeyError:
pass
for k, j in zip(x, y):
if k is not j:
y = tuple(y)
break
else:
y = x
return y
d[tuple] = _deepcopy_tuple
def _deepcopy_dict(x, memo, deepcopy=deepcopy):
y = {}
memo[id(x)] = y
for key, value in x.items():
y[deepcopy(key, memo)] = deepcopy(value, memo)
return y
d[dict] = _deepcopy_dict
if PyStringMap is not None:
d[PyStringMap] = _deepcopy_dict
分析:调用deepcopy进行递归复制可变类型中的元素,返回复制填充完毕后的新y。尤其要注意的是tuple是否包含可变类型,如果不包含就返回x本身,如果包含,就返回新生成的tuple。
深浅拷贝区别:
ele = [4, 5]
mu_list = [1, 2, 3, ele]
mu_dic = {1: 1, 2: 2}
copy_mu_list = copy.copy(mu_list)
deepcopy_mu_list = copy.deepcopy(mu_list)
mu_list[3][0] = 999
if copy_mu_list == [1, 2, 3, [999, 5]]:
print('浅拷贝')
if deepcopy_mu_list == [1, 2, 3, [4, 5]]:
print('深拷贝')
结论:运行后,输出两个string——修改了列表里头的列表,发现只有浅拷贝受到了影响,即浅拷贝把ele照搬过去挂个名字,而深拷贝还进行了ele内部的搬运,生产了一个新的ele。
结束语
深浅拷贝——本质在于其是否使用了递归来进行复制。