copy.py 10.7 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

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
17
  extent possible) inserts *the same objects* into it that the
18 19 20 21 22 23 24 25
  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
for t in (type(None), int, long, float, bool, str, tuple,
103
          frozenset, type, xrange, types.ClassType,
104
          types.BuiltinFunctionType, type(Ellipsis),
Tim Peters's avatar
Tim Peters committed
105
          types.FunctionType):
106 107 108 109 110 111 112 113 114 115 116 117
    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
118
    return x.copy()
119
if PyStringMap is not None:
120
    d[PyStringMap] = _copy_with_copy_method
121 122

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

del d

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

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

    if memo is None:
        memo = {}
152

Tim Peters's avatar
Tim Peters committed
153
    d = id(x)
154 155 156 157 158 159 160 161 162 163
    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
164
        try:
165 166
            issc = issubclass(cls, type)
        except TypeError: # cls is not a class (old Boost; see SF #502085)
167 168
            issc = 0
        if issc:
169
            y = _deepcopy_atomic(x, memo)
170
        else:
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
            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)
190

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

_deepcopy_dispatch = d = {}

def _deepcopy_atomic(x, memo):
Tim Peters's avatar
Tim Peters committed
198
    return x
199
d[type(None)] = _deepcopy_atomic
200
d[type(Ellipsis)] = _deepcopy_atomic
201 202 203 204
d[int] = _deepcopy_atomic
d[long] = _deepcopy_atomic
d[float] = _deepcopy_atomic
d[bool] = _deepcopy_atomic
205
try:
206
    d[complex] = _deepcopy_atomic
207
except NameError:
208
    pass
209
d[str] = _deepcopy_atomic
210
try:
211
    d[unicode] = _deepcopy_atomic
212
except NameError:
213
    pass
214 215 216 217
try:
    d[types.CodeType] = _deepcopy_atomic
except AttributeError:
    pass
218 219
d[type] = _deepcopy_atomic
d[xrange] = _deepcopy_atomic
220
d[types.ClassType] = _deepcopy_atomic
221
d[types.BuiltinFunctionType] = _deepcopy_atomic
Guido van Rossum's avatar
Guido van Rossum committed
222
d[types.FunctionType] = _deepcopy_atomic
223 224

def _deepcopy_list(x, memo):
Tim Peters's avatar
Tim Peters committed
225 226 227 228 229
    y = []
    memo[id(x)] = y
    for a in x:
        y.append(deepcopy(a, memo))
    return y
230
d[list] = _deepcopy_list
231 232

def _deepcopy_tuple(x, memo):
Tim Peters's avatar
Tim Peters committed
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
    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
249
d[tuple] = _deepcopy_tuple
250 251

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

261
def _keep_alive(x, memo):
Tim Peters's avatar
Tim Peters committed
262 263 264 265 266 267 268 269 270 271 272 273 274 275
    """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]
276

277
def _deepcopy_inst(x, memo):
Tim Peters's avatar
Tim Peters committed
278 279 280 281 282
    if hasattr(x, '__deepcopy__'):
        return x.__deepcopy__(memo)
    if hasattr(x, '__getinitargs__'):
        args = x.__getinitargs__()
        args = deepcopy(args, memo)
283
        y = x.__class__(*args)
Tim Peters's avatar
Tim Peters committed
284 285 286 287 288 289 290 291 292 293 294 295 296 297
    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
298 299
d[types.InstanceType] = _deepcopy_inst

300
def _reconstruct(x, info, deep, memo=None):
301 302 303
    if isinstance(info, str):
        return x
    assert isinstance(info, tuple)
304 305
    if memo is None:
        memo = {}
306
    n = len(info)
307
    assert n in (2, 3, 4, 5)
308 309 310 311 312
    callable, args = info[:2]
    if n > 2:
        state = info[2]
    else:
        state = {}
313 314 315 316 317 318 319 320
    if n > 3:
        listiter = info[3]
    else:
        listiter = None
    if n > 4:
        dictiter = info[4]
    else:
        dictiter = None
321
    if deep:
322
        args = deepcopy(args, memo)
323
    y = callable(*args)
324
    memo[id(x)] = y
325 326 327 328 329 330 331 332 333 334 335
    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
336 337
    if state:
        if deep:
338
            state = deepcopy(state, memo)
339 340 341
        if hasattr(y, '__setstate__'):
            y.__setstate__(state)
        else:
342 343 344 345 346 347 348 349 350
            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)
351 352
    return y

353 354 355 356
del d

del types

357 358 359 360
# Helper for instance creation without calling __init__
class _EmptyClass:
    pass

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

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