1"""Generic (shallow and deep) copying operations. 2 3Interface summary: 4 5 import copy 6 7 x = copy.copy(y) # make a shallow copy of y 8 x = copy.deepcopy(y) # make a deep copy of y 9 x = copy.replace(y, a=1, b=2) # new object with fields replaced, as defined by `__replace__` 10 11For module specific errors, copy.Error is raised. 12 13The difference between shallow and deep copying is only relevant for 14compound objects (objects that contain other objects, like lists or 15class instances). 16 17- A shallow copy constructs a new compound object and then (to the 18 extent possible) inserts *the same objects* into it that the 19 original contains. 20 21- A deep copy constructs a new compound object and then, recursively, 22 inserts *copies* into it of the objects found in the original. 23 24Two problems often exist with deep copy operations that don't exist 25with shallow copy operations: 26 27 a) recursive objects (compound objects that, directly or indirectly, 28 contain a reference to themselves) may cause a recursive loop 29 30 b) because deep copy copies *everything* it may copy too much, e.g. 31 administrative data structures that should be shared even between 32 copies 33 34Python's deep copy operation avoids these problems by: 35 36 a) keeping a table of objects already copied during the current 37 copying pass 38 39 b) letting user-defined classes override the copying operation or the 40 set of components copied 41 42This version does not copy types like module, class, function, method, 43nor stack trace, stack frame, nor file, socket, window, nor any 44similar types. 45 46Classes can use the same interfaces to control copying that they use 47to control pickling: they can define methods called __getinitargs__(), 48__getstate__() and __setstate__(). See the documentation for module 49"pickle" for information on these methods. 50""" 51 52import types 53import weakref 54from copyreg import dispatch_table 55 56class Error(Exception): 57 pass 58error = Error # backward compatibility 59 60__all__ = ["Error", "copy", "deepcopy", "replace"] 61 62def copy(x): 63 """Shallow copy operation on arbitrary Python objects. 64 65 See the module's __doc__ string for more info. 66 """ 67 68 cls = type(x) 69 70 copier = _copy_dispatch.get(cls) 71 if copier: 72 return copier(x) 73 74 if issubclass(cls, type): 75 # treat it as a regular class: 76 return _copy_immutable(x) 77 78 copier = getattr(cls, "__copy__", None) 79 if copier is not None: 80 return copier(x) 81 82 reductor = dispatch_table.get(cls) 83 if reductor is not None: 84 rv = reductor(x) 85 else: 86 reductor = getattr(x, "__reduce_ex__", None) 87 if reductor is not None: 88 rv = reductor(4) 89 else: 90 reductor = getattr(x, "__reduce__", None) 91 if reductor: 92 rv = reductor() 93 else: 94 raise Error("un(shallow)copyable object of type %s" % cls) 95 96 if isinstance(rv, str): 97 return x 98 return _reconstruct(x, None, *rv) 99 100 101_copy_dispatch = d = {} 102 103def _copy_immutable(x): 104 return x 105for t in (types.NoneType, int, float, bool, complex, str, tuple, 106 bytes, frozenset, type, range, slice, property, 107 types.BuiltinFunctionType, types.EllipsisType, 108 types.NotImplementedType, types.FunctionType, types.CodeType, 109 weakref.ref): 110 d[t] = _copy_immutable 111 112d[list] = list.copy 113d[dict] = dict.copy 114d[set] = set.copy 115d[bytearray] = bytearray.copy 116 117del d, t 118 119def deepcopy(x, memo=None, _nil=[]): 120 """Deep copy operation on arbitrary Python objects. 121 122 See the module's __doc__ string for more info. 123 """ 124 125 d = id(x) 126 if memo is None: 127 memo = {} 128 else: 129 y = memo.get(d, _nil) 130 if y is not _nil: 131 return y 132 133 cls = type(x) 134 135 copier = _deepcopy_dispatch.get(cls) 136 if copier is not None: 137 y = copier(x, memo) 138 else: 139 if issubclass(cls, type): 140 y = _deepcopy_atomic(x, memo) 141 else: 142 copier = getattr(x, "__deepcopy__", None) 143 if copier is not None: 144 y = copier(memo) 145 else: 146 reductor = dispatch_table.get(cls) 147 if reductor: 148 rv = reductor(x) 149 else: 150 reductor = getattr(x, "__reduce_ex__", None) 151 if reductor is not None: 152 rv = reductor(4) 153 else: 154 reductor = getattr(x, "__reduce__", None) 155 if reductor: 156 rv = reductor() 157 else: 158 raise Error( 159 "un(deep)copyable object of type %s" % cls) 160 if isinstance(rv, str): 161 y = x 162 else: 163 y = _reconstruct(x, memo, *rv) 164 165 # If is its own copy, don't memoize. 166 if y is not x: 167 memo[d] = y 168 _keep_alive(x, memo) # Make sure x lives at least as long as d 169 return y 170 171_deepcopy_dispatch = d = {} 172 173def _deepcopy_atomic(x, memo): 174 return x 175d[types.NoneType] = _deepcopy_atomic 176d[types.EllipsisType] = _deepcopy_atomic 177d[types.NotImplementedType] = _deepcopy_atomic 178d[int] = _deepcopy_atomic 179d[float] = _deepcopy_atomic 180d[bool] = _deepcopy_atomic 181d[complex] = _deepcopy_atomic 182d[bytes] = _deepcopy_atomic 183d[str] = _deepcopy_atomic 184d[types.CodeType] = _deepcopy_atomic 185d[type] = _deepcopy_atomic 186d[range] = _deepcopy_atomic 187d[types.BuiltinFunctionType] = _deepcopy_atomic 188d[types.FunctionType] = _deepcopy_atomic 189d[weakref.ref] = _deepcopy_atomic 190d[property] = _deepcopy_atomic 191 192def _deepcopy_list(x, memo, deepcopy=deepcopy): 193 y = [] 194 memo[id(x)] = y 195 append = y.append 196 for a in x: 197 append(deepcopy(a, memo)) 198 return y 199d[list] = _deepcopy_list 200 201def _deepcopy_tuple(x, memo, deepcopy=deepcopy): 202 y = [deepcopy(a, memo) for a in x] 203 # We're not going to put the tuple in the memo, but it's still important we 204 # check for it, in case the tuple contains recursive mutable structures. 205 try: 206 return memo[id(x)] 207 except KeyError: 208 pass 209 for k, j in zip(x, y): 210 if k is not j: 211 y = tuple(y) 212 break 213 else: 214 y = x 215 return y 216d[tuple] = _deepcopy_tuple 217 218def _deepcopy_dict(x, memo, deepcopy=deepcopy): 219 y = {} 220 memo[id(x)] = y 221 for key, value in x.items(): 222 y[deepcopy(key, memo)] = deepcopy(value, memo) 223 return y 224d[dict] = _deepcopy_dict 225 226def _deepcopy_method(x, memo): # Copy instance methods 227 return type(x)(x.__func__, deepcopy(x.__self__, memo)) 228d[types.MethodType] = _deepcopy_method 229 230del d 231 232def _keep_alive(x, memo): 233 """Keeps a reference to the object x in the memo. 234 235 Because we remember objects by their id, we have 236 to assure that possibly temporary objects are kept 237 alive by referencing them. 238 We store a reference at the id of the memo, which should 239 normally not be used unless someone tries to deepcopy 240 the memo itself... 241 """ 242 try: 243 memo[id(memo)].append(x) 244 except KeyError: 245 # aha, this is the first one :-) 246 memo[id(memo)]=[x] 247 248def _reconstruct(x, memo, func, args, 249 state=None, listiter=None, dictiter=None, 250 *, deepcopy=deepcopy): 251 deep = memo is not None 252 if deep and args: 253 args = (deepcopy(arg, memo) for arg in args) 254 y = func(*args) 255 if deep: 256 memo[id(x)] = y 257 258 if state is not None: 259 if deep: 260 state = deepcopy(state, memo) 261 if hasattr(y, '__setstate__'): 262 y.__setstate__(state) 263 else: 264 if isinstance(state, tuple) and len(state) == 2: 265 state, slotstate = state 266 else: 267 slotstate = None 268 if state is not None: 269 y.__dict__.update(state) 270 if slotstate is not None: 271 for key, value in slotstate.items(): 272 setattr(y, key, value) 273 274 if listiter is not None: 275 if deep: 276 for item in listiter: 277 item = deepcopy(item, memo) 278 y.append(item) 279 else: 280 for item in listiter: 281 y.append(item) 282 if dictiter is not None: 283 if deep: 284 for key, value in dictiter: 285 key = deepcopy(key, memo) 286 value = deepcopy(value, memo) 287 y[key] = value 288 else: 289 for key, value in dictiter: 290 y[key] = value 291 return y 292 293del types, weakref 294 295 296def replace(obj, /, **changes): 297 """Return a new object replacing specified fields with new values. 298 299 This is especially useful for immutable objects, like named tuples or 300 frozen dataclasses. 301 """ 302 cls = obj.__class__ 303 func = getattr(cls, '__replace__', None) 304 if func is None: 305 raise TypeError(f"replace() does not support {cls.__name__} objects") 306 return func(obj, **changes) 307