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