| """Generic (shallow and deep) copying operations. |
| |
| Interface summary: |
| |
| import copy |
| |
| x = copy.copy(y)# make a shallow copy of y |
| x = copy.deepcopy(y)# make a deep copy of y |
| |
| For module specific errors, copy.error is raised. |
| |
| The difference between shallow and deep copying is only relevant for |
| compound objects (objects that contain other objects, like lists or |
| class instances). |
| |
| - A shallow copy constructs a new compound object and then (to the |
| extent possible) inserts *the same objects* into in that the |
| original contains. |
| |
| - A deep copy constructs a new compound object and then, recursively, |
| inserts *copies* into it of the objects found in the original. |
| |
| Two problems often exist with deep copy operations that don't exist |
| with shallow copy operations: |
| |
| a) recursive objects (compound objects that, directly or indirectly, |
| contain a reference to themselves) may cause a recursive loop |
| |
| b) because deep copy copies *everything* it may copy too much, e.g. |
| administrative data structures that should be shared even between |
| copies |
| |
| Python's deep copy operation avoids these problems by: |
| |
| a) keeping a table of objects already copied during the current |
| copying pass |
| |
| b) letting user-defined classes override the copying operation or the |
| set of components copied |
| |
| This version does not copy types like module, class, function, method, |
| nor stack trace, stack frame, nor file, socket, window, nor array, nor |
| any similar types. |
| |
| Classes can use the same interfaces to control copying that they use |
| to control pickling: they can define methods called __getinitargs__(), |
| __getstate__() and __setstate__(). See the documentation for module |
| "pickle" for information on these methods. |
| """ |
| |
| # XXX need to support copy_reg here too... |
| |
| import types |
| |
| classError(Exception): |
| pass |
| error=Error# backward compatibility |
| |
| def copy(x): |
| """Shallow copy operation on arbitrary Python objects. |
| |
| See the module's __doc__ string for more info. |
| """ |
| |
| try: |
| copierfunction= _copy_dispatch[type(x)] |
| exceptKeyError: |
| try: |
| copier= x.__copy__ |
| exceptAttributeError: |
| raise error, \ |
| "un(shallow)copyable object of type %s"% type(x) |
| y= copier() |
| else: |
| y= copierfunction(x) |
| return y |
| |
| _copy_dispatch= d={} |
| |
| def _copy_atomic(x): |
| return x |
| d[types.NoneType]= _copy_atomic |
| d[types.IntType]= _copy_atomic |
| d[types.LongType]= _copy_atomic |
| d[types.FloatType]= _copy_atomic |
| d[types.StringType]= _copy_atomic |
| d[types.UnicodeType]= _copy_atomic |
| try: |
| d[types.CodeType]= _copy_atomic |
| exceptAttributeError: |
| pass |
| d[types.TypeType]= _copy_atomic |
| d[types.XRangeType]= _copy_atomic |
| d[types.ClassType]= _copy_atomic |
| |
| def _copy_list(x): |
| return x[:] |
| d[types.ListType]= _copy_list |
| |
| def _copy_tuple(x): |
| return x[:] |
| d[types.TupleType]= _copy_tuple |
| |
| def _copy_dict(x): |
| return x.copy() |
| d[types.DictionaryType]= _copy_dict |
| |
| def _copy_inst(x): |
| if hasattr(x,'__copy__'): |
| return x.__copy__() |
| if hasattr(x,'__getinitargs__'): |
| args= x.__getinitargs__() |
| y= apply(x.__class__, args) |
| else: |
| y=_EmptyClass() |
| y.__class__= x.__class__ |
| if hasattr(x,'__getstate__'): |
| state= x.__getstate__() |
| else: |
| state= x.__dict__ |
| if hasattr(y,'__setstate__'): |
| y.__setstate__(state) |
| else: |
| y.__dict__.update(state) |
| return y |
| d[types.InstanceType]= _copy_inst |
| |
| del d |
| |
| def deepcopy(x, memo=None): |
| """Deep copy operation on arbitrary Python objects. |
| |
| See the module's __doc__ string for more info. |
| """ |
| |
| if memoisNone: |
| memo={} |
| d= id(x) |
| if memo.has_key(d): |
| return memo[d] |
| try: |
| copierfunction= _deepcopy_dispatch[type(x)] |
| exceptKeyError: |
| try: |
| copier= x.__deepcopy__ |
| exceptAttributeError: |
| raise error, \ |
| "un-deep-copyable object of type %s"% type(x) |
| y= copier(memo) |
| else: |
| y= copierfunction(x, memo) |
| memo[d]= y |
| return y |
| |
| _deepcopy_dispatch= d={} |
| |
| def _deepcopy_atomic(x, memo): |
| return x |
| d[types.NoneType]= _deepcopy_atomic |
| d[types.IntType]= _deepcopy_atomic |
| d[types.LongType]= _deepcopy_atomic |
| d[types.FloatType]= _deepcopy_atomic |
| d[types.StringType]= _deepcopy_atomic |
| d[types.UnicodeType]= _deepcopy_atomic |
| d[types.CodeType]= _deepcopy_atomic |
| d[types.TypeType]= _deepcopy_atomic |
| d[types.XRangeType]= _deepcopy_atomic |
| |
| def _deepcopy_list(x, memo): |
| y=[] |
| memo[id(x)]= y |
| for ain x: |
| y.append(deepcopy(a, memo)) |
| return y |
| d[types.ListType]= _deepcopy_list |
| |
| def _deepcopy_tuple(x, memo): |
| y=[] |
| for ain x: |
| y.append(deepcopy(a, memo)) |
| d= id(x) |
| try: |
| return memo[d] |
| exceptKeyError: |
| pass |
| for iin range(len(x)): |
| if x[i]isnot y[i]: |
| y= tuple(y) |
| break |
| else: |
| y= x |
| memo[d]= y |
| return y |
| d[types.TupleType]= _deepcopy_tuple |
| |
| def _deepcopy_dict(x, memo): |
| y={} |
| memo[id(x)]= y |
| for keyin x.keys(): |
| y[deepcopy(key, memo)]= deepcopy(x[key], memo) |
| return y |
| d[types.DictionaryType]= _deepcopy_dict |
| |
| 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) |
| exceptKeyError: |
| # aha, this is the first one :-) |
| memo[id(memo)]=[x] |
| |
| def _deepcopy_inst(x, memo): |
| if hasattr(x,'__deepcopy__'): |
| return x.__deepcopy__(memo) |
| if hasattr(x,'__getinitargs__'): |
| args= x.__getinitargs__() |
| _keep_alive(args, memo) |
| args= deepcopy(args, memo) |
| y= apply(x.__class__, args) |
| else: |
| y=_EmptyClass() |
| y.__class__= x.__class__ |
| memo[id(x)]= y |
| if hasattr(x,'__getstate__'): |
| state= x.__getstate__() |
| _keep_alive(state, memo) |
| else: |
| state= x.__dict__ |
| state= deepcopy(state, memo) |
| if hasattr(y,'__setstate__'): |
| y.__setstate__(state) |
| else: |
| y.__dict__.update(state) |
| return y |
| d[types.InstanceType]= _deepcopy_inst |
| |
| del d |
| |
| del types |
| |
| # Helper for instance creation without calling __init__ |
| class_EmptyClass: |
| pass |
| |
| def _test(): |
| l=[None,1,2L,3.14,'xyzzy',(1,2L),[3.14,'abc'], |
| {'abc':'ABC'},(),[],{}] |
| l1= copy(l) |
| print l1==l |
| l1= map(copy, l) |
| print l1==l |
| l1= deepcopy(l) |
| print l1==l |
| class C: |
| def __init__(self, arg=None): |
| self.a=1 |
| self.arg= arg |
| if __name__=='__main__': |
| import sys |
| file= sys.argv[0] |
| else: |
| file= __file__ |
| self.fp= open(file) |
| self.fp.close() |
| def __getstate__(self): |
| return{'a': self.a,'arg': self.arg} |
| def __setstate__(self, state): |
| for keyin state.keys(): |
| setattr(self, key, state[key]) |
| def __deepcopy__(self, memo=None): |
| new= self.__class__(deepcopy(self.arg, memo)) |
| new.a= self.a |
| return new |
| c= C('argument sketch') |
| l.append(c) |
| l2= copy(l) |
| print l== l2 |
| print l |
| print l2 |
| l2= deepcopy(l) |
| print l== l2 |
| print l |
| print l2 |
| l.append({l[1]: l,'xyz': l[2]}) |
| l3= copy(l) |
| import repr |
| print map(repr.repr, l) |
| print map(repr.repr, l1) |
| print map(repr.repr, l2) |
| print map(repr.repr, l3) |
| l3= deepcopy(l) |
| import repr |
| print map(repr.repr, l) |
| print map(repr.repr, l1) |
| print map(repr.repr, l2) |
| print map(repr.repr, l3) |
| |
| if __name__=='__main__': |
| _test() |