copy.py 10.8 KB
Newer Older
1
"""Generic (shallow and deep) copying operations.
2

3 4
Interface summary:

Tim Peters's avatar
Tim Peters committed
5
        import copy
6

Tim Peters's avatar
Tim Peters committed
7 8
        x = copy.copy(y)        # make a shallow copy of y
        x = copy.deepcopy(y)    # make a deep copy of y
9

10
For module specific errors, copy.Error is raised.
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

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:

26
 a) recursive objects (compound objects that, directly or indirectly,
27 28
    contain a reference to themselves) may cause a recursive loop

29
 b) because deep copy copies *everything* it may copy too much, e.g.
30 31 32 33 34
    administrative data structures that should be shared even between
    copies

Python's deep copy operation avoids these problems by:

35 36
 a) keeping a table of objects already copied during the current
    copying pass
37

38
 b) letting user-defined classes override the copying operation or the
39 40 41 42 43 44 45 46
    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__(),
47
__getstate__() and __setstate__().  See the documentation for module
48 49
"pickle" for information on these methods.
"""
50 51

import types
52
from copy_reg import dispatch_table
53

54
class Error(Exception):
Tim Peters's avatar
Tim Peters committed
55 56
    pass
error = Error   # backward compatibility
57

58 59 60 61 62
try:
    from org.python.core import PyStringMap
except ImportError:
    PyStringMap = None

63
__all__ = ["Error", "copy", "deepcopy"]
64

65
def copy(x):
Tim Peters's avatar
Tim Peters committed
66 67 68 69 70
    """Shallow copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.
    """

71 72 73 74 75 76 77 78 79 80 81
    cls = type(x)

    copier = _copy_dispatch.get(cls)
    if copier:
        return copier(x)

    copier = getattr(cls, "__copy__", None)
    if copier:
        return copier(x)

    reductor = dispatch_table.get(cls)
82 83 84 85 86 87 88 89 90 91 92 93 94 95
    if reductor:
        rv = reductor(x)
    else:
        reductor = getattr(x, "__reduce_ex__", None)
        if reductor:
            rv = reductor(2)
        else:
            reductor = getattr(x, "__reduce__", None)
            if reductor:
                rv = reductor()
            else:
                raise Error("un(shallow)copyable object of type %s" % cls)

    return _reconstruct(x, rv, 0)
Tim Peters's avatar
Tim Peters committed
96

97

98 99
_copy_dispatch = d = {}

100
def _copy_immutable(x):
Tim Peters's avatar
Tim Peters committed
101
    return x
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
for t in (types.NoneType, int, long, float, bool, str, tuple,
          frozenset, type, xrange, types.ClassType,
          types.BuiltinFunctionType):
    d[t] = _copy_immutable
for name in ("ComplexType", "UnicodeType", "CodeType"):
    t = getattr(types, name, None)
    if t is not None:
        d[t] = _copy_immutable

def _copy_with_constructor(x):
    return type(x)(x)
for t in (list, dict, set):
    d[t] = _copy_with_constructor

def _copy_with_copy_method(x):
Tim Peters's avatar
Tim Peters committed
117
    return x.copy()
118
if PyStringMap is not None:
119
    d[PyStringMap] = _copy_with_copy_method
120 121

def _copy_inst(x):
Tim Peters's avatar
Tim Peters committed
122 123 124 125
    if hasattr(x, '__copy__'):
        return x.__copy__()
    if hasattr(x, '__getinitargs__'):
        args = x.__getinitargs__()
126
        y = x.__class__(*args)
Tim Peters's avatar
Tim Peters committed
127 128 129 130 131 132 133 134 135 136 137 138
    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
139 140 141 142
d[types.InstanceType] = _copy_inst

del d

143
def deepcopy(x, memo=None, _nil=[]):
Tim Peters's avatar
Tim Peters committed
144 145 146 147 148 149 150
    """Deep copy operation on arbitrary Python objects.

    See the module's __doc__ string for more info.
    """

    if memo is None:
        memo = {}
151

Tim Peters's avatar
Tim Peters committed
152
    d = id(x)
153 154 155 156 157 158 159 160 161 162
    y = memo.get(d, _nil)
    if y is not _nil:
        return y

    cls = type(x)

    copier = _deepcopy_dispatch.get(cls)
    if copier:
        y = copier(x, memo)
    else:
Tim Peters's avatar
Tim Peters committed
163
        try:
164 165
            issc = issubclass(cls, type)
        except TypeError: # cls is not a class (old Boost; see SF #502085)
166 167
            issc = 0
        if issc:
168
            y = _deepcopy_atomic(x, memo)
169
        else:
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
            copier = getattr(x, "__deepcopy__", None)
            if copier:
                y = copier(memo)
            else:
                reductor = dispatch_table.get(cls)
                if reductor:
                    rv = reductor(x)
                else:
                    reductor = getattr(x, "__reduce_ex__", None)
                    if reductor:
                        rv = reductor(2)
                    else:
                        reductor = getattr(x, "__reduce__", None)
                        if reductor:
                            rv = reductor()
                        else:
                            raise Error(
                                "un(deep)copyable object of type %s" % cls)
                y = _reconstruct(x, rv, 1, memo)
189

Tim Peters's avatar
Tim Peters committed
190
    memo[d] = y
191
    _keep_alive(x, memo) # Make sure x lives at least as long as d
Tim Peters's avatar
Tim Peters committed
192
    return y
193 194 195 196

_deepcopy_dispatch = d = {}

def _deepcopy_atomic(x, memo):
Tim Peters's avatar
Tim Peters committed
197
    return x
198 199 200 201
d[types.NoneType] = _deepcopy_atomic
d[types.IntType] = _deepcopy_atomic
d[types.LongType] = _deepcopy_atomic
d[types.FloatType] = _deepcopy_atomic
202
d[types.BooleanType] = _deepcopy_atomic
203 204 205 206
try:
    d[types.ComplexType] = _deepcopy_atomic
except AttributeError:
    pass
207
d[types.StringType] = _deepcopy_atomic
208 209 210 211
try:
    d[types.UnicodeType] = _deepcopy_atomic
except AttributeError:
    pass
212 213 214 215
try:
    d[types.CodeType] = _deepcopy_atomic
except AttributeError:
    pass
216 217
d[types.TypeType] = _deepcopy_atomic
d[types.XRangeType] = _deepcopy_atomic
218
d[types.ClassType] = _deepcopy_atomic
219
d[types.BuiltinFunctionType] = _deepcopy_atomic
220 221

def _deepcopy_list(x, memo):
Tim Peters's avatar
Tim Peters committed
222 223 224 225 226
    y = []
    memo[id(x)] = y
    for a in x:
        y.append(deepcopy(a, memo))
    return y
227 228 229
d[types.ListType] = _deepcopy_list

def _deepcopy_tuple(x, memo):
Tim Peters's avatar
Tim Peters committed
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
    y = []
    for a in x:
        y.append(deepcopy(a, memo))
    d = id(x)
    try:
        return memo[d]
    except KeyError:
        pass
    for i in range(len(x)):
        if x[i] is not y[i]:
            y = tuple(y)
            break
    else:
        y = x
    memo[d] = y
    return y
246 247 248
d[types.TupleType] = _deepcopy_tuple

def _deepcopy_dict(x, memo):
Tim Peters's avatar
Tim Peters committed
249 250
    y = {}
    memo[id(x)] = y
251 252
    for key, value in x.iteritems():
        y[deepcopy(key, memo)] = deepcopy(value, memo)
Tim Peters's avatar
Tim Peters committed
253
    return y
254
d[types.DictionaryType] = _deepcopy_dict
255 256
if PyStringMap is not None:
    d[PyStringMap] = _deepcopy_dict
257

258
def _keep_alive(x, memo):
Tim Peters's avatar
Tim Peters committed
259 260 261 262 263 264 265 266 267 268 269 270 271 272
    """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)
    except KeyError:
        # aha, this is the first one :-)
        memo[id(memo)]=[x]
273

274
def _deepcopy_inst(x, memo):
Tim Peters's avatar
Tim Peters committed
275 276 277 278 279
    if hasattr(x, '__deepcopy__'):
        return x.__deepcopy__(memo)
    if hasattr(x, '__getinitargs__'):
        args = x.__getinitargs__()
        args = deepcopy(args, memo)
280
        y = x.__class__(*args)
Tim Peters's avatar
Tim Peters committed
281 282 283 284 285 286 287 288 289 290 291 292 293 294
    else:
        y = _EmptyClass()
        y.__class__ = x.__class__
    memo[id(x)] = y
    if hasattr(x, '__getstate__'):
        state = x.__getstate__()
    else:
        state = x.__dict__
    state = deepcopy(state, memo)
    if hasattr(y, '__setstate__'):
        y.__setstate__(state)
    else:
        y.__dict__.update(state)
    return y
295 296
d[types.InstanceType] = _deepcopy_inst

297
def _reconstruct(x, info, deep, memo=None):
298 299 300
    if isinstance(info, str):
        return x
    assert isinstance(info, tuple)
301 302
    if memo is None:
        memo = {}
303
    n = len(info)
304
    assert n in (2, 3, 4, 5)
305 306 307 308 309
    callable, args = info[:2]
    if n > 2:
        state = info[2]
    else:
        state = {}
310 311 312 313 314 315 316 317
    if n > 3:
        listiter = info[3]
    else:
        listiter = None
    if n > 4:
        dictiter = info[4]
    else:
        dictiter = None
318
    if deep:
319
        args = deepcopy(args, memo)
320
    y = callable(*args)
321
    memo[id(x)] = y
322 323 324 325 326 327 328 329 330 331 332
    if listiter is not None:
        for item in listiter:
            if deep:
                item = deepcopy(item, memo)
            y.append(item)
    if dictiter is not None:
        for key, value in dictiter:
            if deep:
                key = deepcopy(key, memo)
                value = deepcopy(value, memo)
            y[key] = value
333 334
    if state:
        if deep:
335
            state = deepcopy(state, memo)
336 337 338
        if hasattr(y, '__setstate__'):
            y.__setstate__(state)
        else:
339 340 341 342 343 344 345 346 347
            if isinstance(state, tuple) and len(state) == 2:
                state, slotstate = state
            else:
                slotstate = None
            if state is not None:
                y.__dict__.update(state)
            if slotstate is not None:
                for key, value in slotstate.iteritems():
                    setattr(y, key, value)
348 349
    return y

350 351 352 353
del d

del types

354 355 356 357
# Helper for instance creation without calling __init__
class _EmptyClass:
    pass

358
def _test():
Tim Peters's avatar
Tim Peters committed
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
    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):
381 382
            for key, value in state.iteritems():
                setattr(self, key, value)
383
        def __deepcopy__(self, memo=None):
Tim Peters's avatar
Tim Peters committed
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
            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)
410 411

if __name__ == '__main__':
Tim Peters's avatar
Tim Peters committed
412
    _test()