The assignment expression in Python does not assign an object, but creates a binding between the target and the object. This module provides common shallow and deep copy operations.
Shallow and deep copies are only related to composite objects:
- Shallow copy constructs a new composite object, and then (to the extent possible) inserts a reference to the object in the original object.
- Deep copy constructs a new composite object, and then recursively inserts a copy of the original object (different from the original object).
There are two common problems with deep copies that do not exist in shallow copies:
- Recursive objects (those composite objects that directly or indirectly contain their own references) can cause recursive loops.
- Because deep copy copies anything, it may copy too much content that may be shared in replication.
However, the copy.deepcopy() function avoids these problems by:
- Maintain a dictionary memo that holds previously copied objects.
- Let user-defined classes override copy operations or copy component sets.
The contents that can be copied in this module are compatible with those defined in pickle module.
Shallow copies of dictionaries can be copied through dict.copy(), while shallow copies of lists can be copied through slices_ list = original_ list[:].
Class can control replication by using the same interface that controls serialization. As__ reduce_ex__(version) and__ reduce__ ().
Class can define its own replication implementation by implementing the replication protocol__ copy__ () implement shallow copy__ deepcopy__ () implement deep copy.
Shallow copy
Implementation of shallow copy:
- For common immutable types (such as int, etc.) and variable types (such as list, etc.), from_ copy_ Obtain the copy function from the dispatch map for shallow copy operation.
- Other types, such as custom classes, if implemented__ copy__ () call this method for shallow copy; If the dispatch in the copyreg module_ If the table map has a copy function of this type, call this function for shallow copy; If the serialization protocol (such as _reduce_ex_ () and _reduce_ ()) is implemented, call this method for shallow copy.
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 it is a common type, such as immutable type int, variable type list, # Copy directly through the copy function. if copier: return copier(x) if issubclass(cls, type): # treat it as a regular class: return _copy_immutable(x) # If the object implements the shallow copy protocol, through shallow copy # Special methods to achieve copy operation. copier = getattr(cls, "__copy__", None) if copier is not None: return copier(x) # Get the copy function of the corresponding type from copyreg reductor = dispatch_table.get(cls) if reductor is not None: rv = reductor(x) else: # From__ reduce_ex__ Obtain the corresponding compression method in, # This is the content of the serialization protocol, but it can be used for copy operation, # Classes that inherit object implement this method by default 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 # _ The reconstruct function is mainly used in shallow copy # __ reduce_ex__ Returned create object function and corresponding parameters # To copy the object. For details, refer to the serialization protocol. return _reconstruct(x, None, *rv) # Mapping of types and copy functions _copy_dispatch = d = {} # Copy function of immutable type 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 # Copy function of variable type 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) # Some relations with state and listier are omitted here # Content related to dictiter ...... return y
Deep copy
Deep copy implementation:
- In order to solve the two problems of deep copy mentioned above, a dictionary memo is maintained. If it has been copied, it will be stored in this dictionary and obtained directly from this dictionary.
- Otherwise, map from deep copy_ deepcopy_ Obtain the corresponding type of replication function in dispatch, and perform the copy operation through the replication function.
- If there is no copy function for the object, it is consistent with the shallow copy operation.
- Finally, judge whether the copied object is the original object. If not, store it in memo to avoid recursive loop.
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 is its own copy, don't memoize. if y is not x: memo[d] = y _keep_alive(x, memo) # Make sure x lives at least as long as d return y _deepcopy_dispatch = d = {} # Copy function of immutable type, # It can also be seen here that for immutable types, # There is no difference between light copy and deep copy. 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 # Copy function of variable type def _deepcopy_list(x, memo, deepcopy=deepcopy): y = [] memo[id(x)] = y append = y.append for a in x: # This seems to be a recursive copy, but it is actually through memo # Avoided. 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 def _deepcopy_method(x, memo): # Copy instance methods 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: # aha, this is the first one :-) 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 # Some relations with state and listier are omitted here # Content related to dictiter ...... return y