Python中的赋值表达式并不会赋值对象,只是创建目标和对象之间的绑定。本模块提供通用浅和深拷贝操作。
浅拷贝和深拷贝只和复合对象有关:
- 浅拷贝构造一个新的复合对象,然后(在可能的范围内)向其中插入对原始对象中的对象的引用。
- 深拷贝构造一个新的复合对象,然后递归地向其中插入对原始对象的复制(不同于原对象)。
两个常见于深拷贝而不存在于浅拷贝的问题:
- 递归对象(那些直接或间接包含自身引用的复合对象)可能导致递归循环。
- 因为深拷贝复制任何内容,它可能复制太多内容,而这些内容可能用于在复制中共享。
但copy.deepcopy() 函数避免了这些问题,通过:
- 维护一个字典memo,这个字典保存了之前复制过的对象。
- 让用户自定义类重写复制操作或复制组件集。
本模块可复制的内容和pickle 模块所定义兼容。
字典的浅拷贝可通过dict.copy() ,而列表的浅拷贝可通过切片copied_list = original_list[:] 。
类可通过使用控制序列化相同的接口控制复制。如__reduce_ex__(version) 和__reduce__() 。
类可以通过实现复制协议定义自身复制实现。__copy__() 实现浅拷贝,__deepcopy__() 实现深拷贝。
浅拷贝
浅拷贝的实现:
- 对于常见的不可变类型(如
int 等)和可变类型(如list 等)等,通过类型从_copy_dispatch 映射中获取复制函数进行浅拷贝操作。 - 其他类型如自定义的类,如果实现了
__copy__() 则调用该方法进行浅拷贝;如果copyreg 模块中的dispatch_table 映射有该类型的复制函数,则调用该函数进行浅拷贝;如果实现了序列化协议(如__reduce_ex__() 和__reduce__() )则调用该方法进行浅拷贝。
import types
import weakref
from copyreg import dispatch_table
def copy(x):
"""Shallow copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
"""
cls = type(x)
copier = _copy_dispatch.get(cls)
if copier:
return copier(x)
if issubclass(cls, type):
return _copy_immutable(x)
copier = getattr(cls, "__copy__", None)
if copier is not None:
return copier(x)
reductor = dispatch_table.get(cls)
if reductor is not None:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor is not None:
rv = reductor(4)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
else:
raise Error("un(shallow)copyable object of type %s" % cls)
if isinstance(rv, str):
return x
return _reconstruct(x, None, *rv)
_copy_dispatch = d = {}
def _copy_immutable(x):
return x
for t in (type(None), int, float, bool, complex, str, tuple,
bytes, frozenset, type, range, slice,
types.BuiltinFunctionType, type(Ellipsis), type(NotImplemented),
types.FunctionType, weakref.ref):
d[t] = _copy_immutable
t = getattr(types, "CodeType", None)
if t is not None:
d[t] = _copy_immutable
d[list] = list.copy
d[dict] = dict.copy
d[set] = set.copy
d[bytearray] = bytearray.copy
if PyStringMap is not None:
d[PyStringMap] = PyStringMap.copy
del d, t
def _reconstruct(x, memo, func, args,
state=None, listiter=None, dictiter=None,
deepcopy=deepcopy):
deep = memo is not None
if deep and args:
args = (deepcopy(arg, memo) for arg in args)
y = func(*args)
……
return y
深拷贝
深拷贝实现:
- 为了解决上面说的深拷贝的两个问题,维护了一个字典
memo ,如果已经拷贝过,那么将会存储在这个字典中,直接从这个字典中获取。 - 否则,则从深拷贝映射
_deepcopy_dispatch 中获取相应类型的复制函数,通过复制函数进行拷贝操作。 - 若没有对象的复制函数,则和浅拷贝操作一致。
- 最后判断拷贝的对象是否是原对象,如果不是则存储到
memo 中,避免递归循环。
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 is not None:
y = copier(x, memo)
else:
if issubclass(cls, type):
y = _deepcopy_atomic(x, memo)
else:
copier = getattr(x, "__deepcopy__", None)
if copier is not None:
y = copier(memo)
else:
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor is not None:
rv = reductor(4)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
else:
raise Error(
"un(deep)copyable object of type %s" % cls)
if isinstance(rv, str):
y = x
else:
y = _reconstruct(x, memo, *rv)
if y is not x:
memo[d] = y
_keep_alive(x, memo)
return y
_deepcopy_dispatch = d = {}
def _deepcopy_atomic(x, memo):
return x
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
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]
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
def _deepcopy_method(x, memo):
return type(x)(x.__func__, deepcopy(x.__self__, memo))
d[types.MethodType] = _deepcopy_method
del 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:
memo[id(memo)]=[x]
def _reconstruct(x, memo, func, args,
state=None, listiter=None, dictiter=None,
deepcopy=deepcopy):
deep = memo is not None
if deep and args:
args = (deepcopy(arg, memo) for arg in args)
y = func(*args)
if deep:
memo[id(x)] = y
……
return y
|