source: python/trunk/Lib/copy.py@ 12

Last change on this file since 12 was 2, checked in by Yuri Dario, 15 years ago

Initial import for vendor code.

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