Commit | Line | Data |
---|---|---|
920dae64 AT |
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 array, nor | |
43 | any 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 | from copy_reg import dispatch_table | |
53 | ||
54 | class Error(Exception): | |
55 | pass | |
56 | error = Error # backward compatibility | |
57 | ||
58 | try: | |
59 | from org.python.core import PyStringMap | |
60 | except ImportError: | |
61 | PyStringMap = None | |
62 | ||
63 | __all__ = ["Error", "copy", "deepcopy"] | |
64 | ||
65 | import inspect | |
66 | def _getspecial(cls, name): | |
67 | for basecls in inspect.getmro(cls): | |
68 | try: | |
69 | return basecls.__dict__[name] | |
70 | except: | |
71 | pass | |
72 | else: | |
73 | return None | |
74 | ||
75 | def copy(x): | |
76 | """Shallow copy operation on arbitrary Python objects. | |
77 | ||
78 | See the module's __doc__ string for more info. | |
79 | """ | |
80 | ||
81 | cls = type(x) | |
82 | ||
83 | copier = _copy_dispatch.get(cls) | |
84 | if copier: | |
85 | return copier(x) | |
86 | ||
87 | copier = _getspecial(cls, "__copy__") | |
88 | if copier: | |
89 | return copier(x) | |
90 | ||
91 | reductor = dispatch_table.get(cls) | |
92 | if reductor: | |
93 | rv = reductor(x) | |
94 | else: | |
95 | reductor = getattr(x, "__reduce_ex__", None) | |
96 | if reductor: | |
97 | rv = reductor(2) | |
98 | else: | |
99 | reductor = getattr(x, "__reduce__", None) | |
100 | if reductor: | |
101 | rv = reductor() | |
102 | else: | |
103 | copier = getattr(x, "__copy__", None) | |
104 | if copier: | |
105 | return copier() | |
106 | raise Error("un(shallow)copyable object of type %s" % cls) | |
107 | ||
108 | return _reconstruct(x, rv, 0) | |
109 | ||
110 | ||
111 | _copy_dispatch = d = {} | |
112 | ||
113 | def _copy_immutable(x): | |
114 | return x | |
115 | for t in (types.NoneType, int, long, float, bool, str, tuple, | |
116 | frozenset, type, xrange, types.ClassType, | |
117 | types.BuiltinFunctionType): | |
118 | d[t] = _copy_immutable | |
119 | for name in ("ComplexType", "UnicodeType", "CodeType"): | |
120 | t = getattr(types, name, None) | |
121 | if t is not None: | |
122 | d[t] = _copy_immutable | |
123 | ||
124 | def _copy_with_constructor(x): | |
125 | return type(x)(x) | |
126 | for t in (list, dict, set): | |
127 | d[t] = _copy_with_constructor | |
128 | ||
129 | def _copy_with_copy_method(x): | |
130 | return x.copy() | |
131 | if PyStringMap is not None: | |
132 | d[PyStringMap] = _copy_with_copy_method | |
133 | ||
134 | def _copy_inst(x): | |
135 | if hasattr(x, '__copy__'): | |
136 | return x.__copy__() | |
137 | if hasattr(x, '__getinitargs__'): | |
138 | args = x.__getinitargs__() | |
139 | y = x.__class__(*args) | |
140 | else: | |
141 | y = _EmptyClass() | |
142 | y.__class__ = x.__class__ | |
143 | if hasattr(x, '__getstate__'): | |
144 | state = x.__getstate__() | |
145 | else: | |
146 | state = x.__dict__ | |
147 | if hasattr(y, '__setstate__'): | |
148 | y.__setstate__(state) | |
149 | else: | |
150 | y.__dict__.update(state) | |
151 | return y | |
152 | d[types.InstanceType] = _copy_inst | |
153 | ||
154 | del d | |
155 | ||
156 | def deepcopy(x, memo=None, _nil=[]): | |
157 | """Deep copy operation on arbitrary Python objects. | |
158 | ||
159 | See the module's __doc__ string for more info. | |
160 | """ | |
161 | ||
162 | if memo is None: | |
163 | memo = {} | |
164 | ||
165 | d = id(x) | |
166 | y = memo.get(d, _nil) | |
167 | if y is not _nil: | |
168 | return y | |
169 | ||
170 | cls = type(x) | |
171 | ||
172 | copier = _deepcopy_dispatch.get(cls) | |
173 | if copier: | |
174 | y = copier(x, memo) | |
175 | else: | |
176 | try: | |
177 | issc = issubclass(cls, type) | |
178 | except TypeError: # cls is not a class (old Boost; see SF #502085) | |
179 | issc = 0 | |
180 | if issc: | |
181 | y = _deepcopy_atomic(x, memo) | |
182 | else: | |
183 | copier = _getspecial(cls, "__deepcopy__") | |
184 | if copier: | |
185 | y = copier(x, memo) | |
186 | else: | |
187 | reductor = dispatch_table.get(cls) | |
188 | if reductor: | |
189 | rv = reductor(x) | |
190 | else: | |
191 | reductor = getattr(x, "__reduce_ex__", None) | |
192 | if reductor: | |
193 | rv = reductor(2) | |
194 | else: | |
195 | reductor = getattr(x, "__reduce__", None) | |
196 | if reductor: | |
197 | rv = reductor() | |
198 | else: | |
199 | copier = getattr(x, "__deepcopy__", None) | |
200 | if copier: | |
201 | return copier(memo) | |
202 | raise Error( | |
203 | "un(deep)copyable object of type %s" % cls) | |
204 | y = _reconstruct(x, rv, 1, memo) | |
205 | ||
206 | memo[d] = y | |
207 | _keep_alive(x, memo) # Make sure x lives at least as long as d | |
208 | return y | |
209 | ||
210 | _deepcopy_dispatch = d = {} | |
211 | ||
212 | def _deepcopy_atomic(x, memo): | |
213 | return x | |
214 | d[types.NoneType] = _deepcopy_atomic | |
215 | d[types.IntType] = _deepcopy_atomic | |
216 | d[types.LongType] = _deepcopy_atomic | |
217 | d[types.FloatType] = _deepcopy_atomic | |
218 | d[types.BooleanType] = _deepcopy_atomic | |
219 | try: | |
220 | d[types.ComplexType] = _deepcopy_atomic | |
221 | except AttributeError: | |
222 | pass | |
223 | d[types.StringType] = _deepcopy_atomic | |
224 | try: | |
225 | d[types.UnicodeType] = _deepcopy_atomic | |
226 | except AttributeError: | |
227 | pass | |
228 | try: | |
229 | d[types.CodeType] = _deepcopy_atomic | |
230 | except AttributeError: | |
231 | pass | |
232 | d[types.TypeType] = _deepcopy_atomic | |
233 | d[types.XRangeType] = _deepcopy_atomic | |
234 | d[types.ClassType] = _deepcopy_atomic | |
235 | d[types.BuiltinFunctionType] = _deepcopy_atomic | |
236 | ||
237 | def _deepcopy_list(x, memo): | |
238 | y = [] | |
239 | memo[id(x)] = y | |
240 | for a in x: | |
241 | y.append(deepcopy(a, memo)) | |
242 | return y | |
243 | d[types.ListType] = _deepcopy_list | |
244 | ||
245 | def _deepcopy_tuple(x, memo): | |
246 | y = [] | |
247 | for a in x: | |
248 | y.append(deepcopy(a, memo)) | |
249 | d = id(x) | |
250 | try: | |
251 | return memo[d] | |
252 | except KeyError: | |
253 | pass | |
254 | for i in range(len(x)): | |
255 | if x[i] is not y[i]: | |
256 | y = tuple(y) | |
257 | break | |
258 | else: | |
259 | y = x | |
260 | memo[d] = y | |
261 | return y | |
262 | d[types.TupleType] = _deepcopy_tuple | |
263 | ||
264 | def _deepcopy_dict(x, memo): | |
265 | y = {} | |
266 | memo[id(x)] = y | |
267 | for key, value in x.iteritems(): | |
268 | y[deepcopy(key, memo)] = deepcopy(value, memo) | |
269 | return y | |
270 | d[types.DictionaryType] = _deepcopy_dict | |
271 | if PyStringMap is not None: | |
272 | d[PyStringMap] = _deepcopy_dict | |
273 | ||
274 | def _keep_alive(x, memo): | |
275 | """Keeps a reference to the object x in the memo. | |
276 | ||
277 | Because we remember objects by their id, we have | |
278 | to assure that possibly temporary objects are kept | |
279 | alive by referencing them. | |
280 | We store a reference at the id of the memo, which should | |
281 | normally not be used unless someone tries to deepcopy | |
282 | the memo itself... | |
283 | """ | |
284 | try: | |
285 | memo[id(memo)].append(x) | |
286 | except KeyError: | |
287 | # aha, this is the first one :-) | |
288 | memo[id(memo)]=[x] | |
289 | ||
290 | def _deepcopy_inst(x, memo): | |
291 | if hasattr(x, '__deepcopy__'): | |
292 | return x.__deepcopy__(memo) | |
293 | if hasattr(x, '__getinitargs__'): | |
294 | args = x.__getinitargs__() | |
295 | args = deepcopy(args, memo) | |
296 | y = x.__class__(*args) | |
297 | else: | |
298 | y = _EmptyClass() | |
299 | y.__class__ = x.__class__ | |
300 | memo[id(x)] = y | |
301 | if hasattr(x, '__getstate__'): | |
302 | state = x.__getstate__() | |
303 | else: | |
304 | state = x.__dict__ | |
305 | state = deepcopy(state, memo) | |
306 | if hasattr(y, '__setstate__'): | |
307 | y.__setstate__(state) | |
308 | else: | |
309 | y.__dict__.update(state) | |
310 | return y | |
311 | d[types.InstanceType] = _deepcopy_inst | |
312 | ||
313 | def _reconstruct(x, info, deep, memo=None): | |
314 | if isinstance(info, str): | |
315 | return x | |
316 | assert isinstance(info, tuple) | |
317 | if memo is None: | |
318 | memo = {} | |
319 | n = len(info) | |
320 | assert n in (2, 3, 4, 5) | |
321 | callable, args = info[:2] | |
322 | if n > 2: | |
323 | state = info[2] | |
324 | else: | |
325 | state = {} | |
326 | if n > 3: | |
327 | listiter = info[3] | |
328 | else: | |
329 | listiter = None | |
330 | if n > 4: | |
331 | dictiter = info[4] | |
332 | else: | |
333 | dictiter = None | |
334 | if deep: | |
335 | args = deepcopy(args, memo) | |
336 | y = callable(*args) | |
337 | memo[id(x)] = y | |
338 | if listiter is not None: | |
339 | for item in listiter: | |
340 | if deep: | |
341 | item = deepcopy(item, memo) | |
342 | y.append(item) | |
343 | if dictiter is not None: | |
344 | for key, value in dictiter: | |
345 | if deep: | |
346 | key = deepcopy(key, memo) | |
347 | value = deepcopy(value, memo) | |
348 | y[key] = value | |
349 | if state: | |
350 | if deep: | |
351 | state = deepcopy(state, memo) | |
352 | if hasattr(y, '__setstate__'): | |
353 | y.__setstate__(state) | |
354 | else: | |
355 | if isinstance(state, tuple) and len(state) == 2: | |
356 | state, slotstate = state | |
357 | else: | |
358 | slotstate = None | |
359 | if state is not None: | |
360 | y.__dict__.update(state) | |
361 | if slotstate is not None: | |
362 | for key, value in slotstate.iteritems(): | |
363 | setattr(y, key, value) | |
364 | return y | |
365 | ||
366 | del d | |
367 | ||
368 | del types | |
369 | ||
370 | # Helper for instance creation without calling __init__ | |
371 | class _EmptyClass: | |
372 | pass | |
373 | ||
374 | def _test(): | |
375 | l = [None, 1, 2L, 3.14, 'xyzzy', (1, 2L), [3.14, 'abc'], | |
376 | {'abc': 'ABC'}, (), [], {}] | |
377 | l1 = copy(l) | |
378 | print l1==l | |
379 | l1 = map(copy, l) | |
380 | print l1==l | |
381 | l1 = deepcopy(l) | |
382 | print l1==l | |
383 | class C: | |
384 | def __init__(self, arg=None): | |
385 | self.a = 1 | |
386 | self.arg = arg | |
387 | if __name__ == '__main__': | |
388 | import sys | |
389 | file = sys.argv[0] | |
390 | else: | |
391 | file = __file__ | |
392 | self.fp = open(file) | |
393 | self.fp.close() | |
394 | def __getstate__(self): | |
395 | return {'a': self.a, 'arg': self.arg} | |
396 | def __setstate__(self, state): | |
397 | for key, value in state.iteritems(): | |
398 | setattr(self, key, value) | |
399 | def __deepcopy__(self, memo=None): | |
400 | new = self.__class__(deepcopy(self.arg, memo)) | |
401 | new.a = self.a | |
402 | return new | |
403 | c = C('argument sketch') | |
404 | l.append(c) | |
405 | l2 = copy(l) | |
406 | print l == l2 | |
407 | print l | |
408 | print l2 | |
409 | l2 = deepcopy(l) | |
410 | print l == l2 | |
411 | print l | |
412 | print l2 | |
413 | l.append({l[1]: l, 'xyz': l[2]}) | |
414 | l3 = copy(l) | |
415 | import repr | |
416 | print map(repr.repr, l) | |
417 | print map(repr.repr, l1) | |
418 | print map(repr.repr, l2) | |
419 | print map(repr.repr, l3) | |
420 | l3 = deepcopy(l) | |
421 | import repr | |
422 | print map(repr.repr, l) | |
423 | print map(repr.repr, l1) | |
424 | print map(repr.repr, l2) | |
425 | print map(repr.repr, l3) | |
426 | ||
427 | if __name__ == '__main__': | |
428 | _test() |