目标:
深拷贝源码分析
0 深浅拷贝基础
关键:
1 深拷贝:拷贝了对象所有元素,包含嵌套元素。是与原来对象无关的全新对象。无关性。
2 浅拷贝: 创建新对象,但新对象中各个元素是原对象对应各个元素的引用。
3 举例如下:
a = [1,[2,3]]
c = copy.copy(a)
那么a与c虽然是两个不同对象(id(a) != id(c)),但是a与c中各个元素是相同的(例如id(a[0]) = id(c[0]) , id[a[1]]=id(c[1]))
那么就是说:
例1: a.append(5)会导致a变成[1,[2,3],5],但由于a与c的地址不同,c并不会有变化。
例 2: a[1].append(6)会导致a变成[1,[2,3,6]],同样也会使得c变成[1,[2,3,6]],因为a[1] 与 c[1]地址相同,a[1]和c[1]实际是同一个对象。
总结:
深浅拷贝都是创建新的对象。但深拷贝包含了嵌套拷贝,拷贝后的对象与原对象无关。
浅拷贝中的每个元素和原对象每个元素相同,如果对元素操作,会使得浅拷贝对应元素发生变化。
参考:
https://www.cnblogs.com/xueli/p/4952063.html
https://www.cnblogs.com/huangbiquan/p/7795152.html
https://blog.csdn.net/w494675608/article/details/82114798
1 主入口
这里使用自己编写的一个demo作为总入口
import copy
def use_copy():
a = [1, [2, 3]]
b = copy.copy(a)
c = copy.deepcopy(a)
assert (id(a) != id(b))
for i, value in enumerate(a):
assert (id(a[i] == id(b[i])))
a.append(5)
assert b == [1, [2, 3]]
assert c == [1, [2, 3]]
a[1].append(6)
assert b == [1, [2, 3, 6]]
assert c == [1, [2, ]]
1.1 进入
E:\developSoftware\python27\Lib\copy.py
代码如下:
def deepcopy(x, memo=None, _nil=[]):
"""Deep copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
"""
if memo is None:
memo = {}
d = id(x)
y = memo.get(d, _nil)
if y is not _nil:
return y
cls = type(x)
copier = _deepcopy_dispatch.get(cls)
if copier:
y = copier(x, memo)
else:
try:
issc = issubclass(cls, type)
except TypeError: # cls is not a class (old Boost; see SF #502085)
issc = 0
if issc:
y = _deepcopy_atomic(x, memo)
else:
copier = getattr(x, "__deepcopy__", None)
if copier:
y = copier(memo)
else:
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor:
rv = reductor(2)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
else:
raise Error(
"un(deep)copyable object of type %s" % cls)
y = _reconstruct(x, rv, 1, memo)
memo[d] = y
_keep_alive(x, memo) # Make sure x lives at least as long as d
return y
分析:
1.1) 变量分析
x的值: <type 'list'>: [1, [2, 3]]
memo的值: None
_nil的值: <type 'list'>: []
id(x)的值: 59984200
y的值: <type 'list'>: []
type(x)的值: <type 'list'>
1.2) 分析
copier = _deepcopy_dispatch.get(cls)
其中:
_deepcopy_dispatch是一个字典,建立了当前待拷贝对象的类型,到拷贝方法的键值对,内容如下:
<type 'dict'>:
{<type 'instance'>: <function _deepcopy_inst at 0x0000000003663828>,
<type 'dict'>: <function _deepcopy_dict at 0x00000000036636D8>,
<type 'complex'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'instancemethod'>: <function _deepcopy_method at 0x0000000003663748>,
<type 'builtin_function_or_method'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'ellipsis'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'weakref'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'function'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'NoneType'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'list'>: <function _deepcopy_list at 0x00000000036635F8>,
<type 'tuple'>: <function _deepcopy_tuple at 0x0000000003663668>,
<type 'type'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'bool'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'float'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'code'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'int'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'long'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'classobj'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'unicode'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'str'>: <function _deepcopy_atomic at 0x0000000003663588>,
<type 'xrange'>: <function _deepcopy_atomic at 0x0000000003663588>}
1.3进入其中:
def _deepcopy_list(x, memo):
y = []
memo[id(x)] = y
for a in x:
y.append(deepcopy(a, memo))
return y
d[list] = _deepcopy_list
分析:
1.3.1) 变量分析
x的值: <type 'list'>: [1, [2, 3]]
memo的值: <type 'dict'>: {}
然后变成:
<type 'dict'>: {59984200L: []}
1.3.2)
第一次循环:
a的值是1
然后递归进入:
def deepcopy(x, memo=None, _nil=[]):
"""Deep copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
"""
if memo is None:
memo = {}
d = id(x)
y = memo.get(d, _nil)
if y is not _nil:
return y
cls = type(x)
copier = _deepcopy_dispatch.get(cls)
if copier:
y = copier(x, memo)
else:
try:
issc = issubclass(cls, type)
except TypeError: # cls is not a class (old Boost; see SF #502085)
issc = 0
if issc:
y = _deepcopy_atomic(x, memo)
else:
copier = getattr(x, "__deepcopy__", None)
if copier:
y = copier(memo)
else:
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor:
rv = reductor(2)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
else:
raise Error(
"un(deep)copyable object of type %s" % cls)
y = _reconstruct(x, rv, 1, memo)
memo[d] = y
_keep_alive(x, memo) # Make sure x lives at least as long as d
return y
分析
1)
x: 1
memo: <type 'dict'>: {59984200L: []}
nil: <type 'list'>: []
d: 47147720
y: <type 'list'>: []
cls: int
copier: <function _deepcopy_atomic at 0x0000000003663588>
def _deepcopy_atomic(x, memo):
return x
y: 1
memo:
<type 'dict'>: {59984200L: [], 47147720L: 1}
2)
调用
_keep_alive(x, memo) # Make sure x lives at least as long as d
具体参见1.2的分析
1.2) 分析
_keep_alive(x, memo) # Make sure x lives at least as long as d
调用:
def _keep_alive(x, memo):
"""Keeps a reference to the object x in the memo.
Because we remember objects by their id, we have
to assure that possibly temporary objects are kept
alive by referencing them.
We store a reference at the id of the memo, which should
normally not be used unless someone tries to deepcopy
the memo itself...
"""
try:
memo[id(memo)].append(x)
except KeyError:
# aha, this is the first one :-)
memo[id(memo)]=[x]
分析:
1.2.1)
x: 1
memo: <type 'dict'>: {59984200L: [], 47147720L: 1}
报错进入
except KeyError:
# aha, this is the first one :-)
memo[id(memo)]=[x]
<type 'dict'>: {59984200L: [], 47147720L: 1}
进入:
<type 'dict'>: {59984200L: [], 47147720L: 1, 60816920L: [1]}
这里设置了本身的值。
返回:
y: 1
1.3) 递归返回
def _deepcopy_list(x, memo):
y = []
memo[id(x)] = y
for a in x:
y.append(deepcopy(a, memo))
return y
分析:
1.3.1)
第一次循环后:
x: <type 'list'>: [1, [2, 3]]
y: <type 'list'>: [1]
memo:
<type 'dict'>: {59984200L: [1], 47147720L: 1, 60816920L: [1]}
第二次循环:
a: <type 'list'>: [2, 3]
进入递归:
def deepcopy(x, memo=None, _nil=[]):
"""Deep copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
"""
if memo is None:
memo = {}
d = id(x)
y = memo.get(d, _nil)
if y is not _nil:
return y
cls = type(x)
copier = _deepcopy_dispatch.get(cls)
if copier:
y = copier(x, memo)
else:
try:
issc = issubclass(cls, type)
except TypeError: # cls is not a class (old Boost; see SF #502085)
issc = 0
if issc:
y = _deepcopy_atomic(x, memo)
else:
copier = getattr(x, "__deepcopy__", None)
if copier:
y = copier(memo)
else:
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor:
rv = reductor(2)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
else:
raise Error(
"un(deep)copyable object of type %s" % cls)
y = _reconstruct(x, rv, 1, memo)
memo[d] = y
_keep_alive(x, memo) # Make sure x lives at least as long as d
return y
分析:
1.3.2)
x: <type 'list'>: [2, 3]
memo:
<type 'dict'>: {59984200L: [1], 47147720L: 1, 60816920L: [1]}
id(x): 58755400
y: <type 'list'>: []
cls: <type 'list'>
copier:
<function _deepcopy_list at 0x00000000036635F8>
进入:
def _deepcopy_list(x, memo):
y = []
memo[id(x)] = y
for a in x:
y.append(deepcopy(a, memo))
return y
分析:
x: <type 'list'>: [2, 3]
memo:
<type 'dict'>: {59984200L: [1], 47147720L: 1, 60816920L: [1]}
新的memo:
<type 'dict'>: {59984200L: [1], 47147720L: 1, 58755400L: [], 60816920L: [1]}
第一次循环:
a: 2
再次进入递归
id(x): 47147696
得到memo:
<type 'dict'>:
{59984200L: [1], 47147720L: 1, 47147696L: 2,
58755400L: [], 60816920L: [1]}
进入_keep_alive(x, memo)
新的memo:
<type 'dict'>:
{59984200L: [1], 47147720L: 1, 47147696L: 2,
58755400L: [], 60816920L: [1, 2]}
第二次循环:
a: 3
新的memo:
<type 'dict'>:
{58755400L: [2, 3], 60816920L: [1, 2, 3],
59984200L: [1],
47147696L: 2, 47147720L: 1, 47147672L: 3}
返回y:
<type 'list'>: [2, 3]
1.4
此时返回到
def deepcopy(x, memo=None, _nil=[]):
"""Deep copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
"""
if memo is None:
memo = {}
d = id(x)
y = memo.get(d, _nil)
if y is not _nil:
return y
cls = type(x)
copier = _deepcopy_dispatch.get(cls)
if copier:
y = copier(x, memo)
else:
try:
issc = issubclass(cls, type)
except TypeError: # cls is not a class (old Boost; see SF #502085)
issc = 0
if issc:
y = _deepcopy_atomic(x, memo)
else:
copier = getattr(x, "__deepcopy__", None)
if copier:
y = copier(memo)
else:
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor:
rv = reductor(2)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
else:
raise Error(
"un(deep)copyable object of type %s" % cls)
y = _reconstruct(x, rv, 1, memo)
memo[d] = y
_keep_alive(x, memo) # Make sure x lives at least as long as d
return y
分析:
刚才返回到
y = copier(x, memo)
y的结果是:
<type 'list'>: [2, 3]
x: <type 'list'>: [2, 3]
id(x): 58755400
得到新的memo:
<type 'dict'>:
{58755400L: [2, 3], 60816920L: [1, 2, 3],
59984200L: [1],
47147696L: 2, 47147720L: 1, 47147672L: 3}
1.4.1)进入:
_keep_alive(x, memo)
def _keep_alive(x, memo):
"""Keeps a reference to the object x in the memo.
Because we remember objects by their id, we have
to assure that possibly temporary objects are kept
alive by referencing them.
We store a reference at the id of the memo, which should
normally not be used unless someone tries to deepcopy
the memo itself...
"""
try:
memo[id(memo)].append(x)
except KeyError:
# aha, this is the first one :-)
memo[id(memo)]=[x]
分析:
x: <type 'list'>: [2, 3]
memo:
<type 'dict'>:
{58755400L: [2, 3],
60816920L: [1, 2, 3],
59984200L: [1],
47147696L: 2, 47147720L: 1, 47147672L: 3}
得到新的memo:
<type 'dict'>:
{58755400L: [2, 3],
60816920L: [1, 2, 3, [2, 3]],
59984200L: [1],
47147696L: 2, 47147720L: 1, 47147672L: 3}
分析:
可以发现:
60816920L: [1, 2, 3, [2, 3]],
也就是说memo本身的id是60816920L,最后memo本身的id这个键对应的值
就变成了新的拷贝之后的值
返回y:
<type 'list'>: [2, 3]
再回到:
def _deepcopy_list(x, memo):
y = []
memo[id(x)] = y
for a in x:
y.append(deepcopy(a, memo))
return y
此时:
y: <type 'list'>: [1, [2, 3]]
此时又回到:
def deepcopy(x, memo)
......
if copier:
y = copier(x, memo)
......
memo[d] = y
_keep_alive(x, memo) # Make sure x lives at least as long as d
return y
分析:
x: <type 'list'>: [1, [2, 3]]
y: <type 'list'>: [1, [2, 3]]
id(x): 59984200
<type 'dict'>:
{58755400L: [2, 3],
60816920L: [1, 2, 3, [2, 3]],
59984200L: [1, [2, 3]],
47147696L: 2, 47147720L: 1, 47147672L: 3}
经过_keep_alive(x, memo)
def _keep_alive(x, memo):
"""Keeps a reference to the object x in the memo.
Because we remember objects by their id, we have
to assure that possibly temporary objects are kept
alive by referencing them.
We store a reference at the id of the memo, which should
normally not be used unless someone tries to deepcopy
the memo itself...
"""
try:
memo[id(memo)].append(x)
except KeyError:
# aha, this is the first one :-)
memo[id(memo)]=[x]
后
memo:
<type 'dict'>:
{58755400L: [2, 3],
60816920L: [1, 2, 3, [2, 3], [1, [2, 3]]],
59984200L: [1, [2, 3]],
47147696L: 2, 47147720L: 1, 47147672L: 3}
可以看到:
60816920L的值从
60816920L: [1, 2, 3, [2, 3]],
变成了
60816920L: [1, 2, 3, [2, 3], [1, [2, 3]]],
2 总结
copy.deepcopy(x, memo=None)方法
def deepcopy(x, memo=None, _nil=[]):
if memo is None:
memo = {}
d = id(x)
y = memo.get(d, _nil)
if y is not _nil:
return y
cls = type(x)
copier = _deepcopy_dispatch.get(cls)
if copier:
y = copier(x, memo)
else:
try:
issc = issubclass(cls, type)
except TypeError: # cls is not a class (old Boost; see SF #502085)
issc = 0
if issc:
y = _deepcopy_atomic(x, memo)
else:
copier = getattr(x, "__deepcopy__", None)
if copier:
y = copier(memo)
else:
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor:
rv = reductor(2)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
else:
raise Error(
"un(deep)copyable object of type %s" % cls)
y = _reconstruct(x, rv, 1, memo)
memo[d] = y
_keep_alive(x, memo) # Make sure x lives at least as long as d
return y
主要的逻辑就是:
1)先判断待拷贝对象x的类型,根据不同类型调用对应的拷贝方法进行拷贝,
例如:对数组,则会遍历每个数组元素,再次递归调用deepcopy方法进行拷贝。
得到拷贝后的结果y后,设置字典memo[id(x)] = y
然后确保在memo中持有对对象x的引用。
2)其中memo是一个备忘录,保存已经拷贝的对象,防止循环拷贝。
所以memo是以待拷贝元素的id为key。
参考:
copy.deepcopy()源码
https://www.2cto.com/kf/201405/302337.html