How can I create a copy of an object in Python?

275,675

Solution 1

To get a fully independent copy of an object you can use the copy.deepcopy() function.

For more details about shallow and deep copying please refer to the other answers to this question and the nice explanation in this answer to a related question.

Solution 2

How can I create a copy of an object in Python?

So, if I change values of the fields of the new object, the old object should not be affected by that.

You mean a mutable object then.

In Python 3, lists get a copy method (in 2, you'd use a slice to make a copy):

>>> a_list = list('abc')
>>> a_copy_of_a_list = a_list.copy()
>>> a_copy_of_a_list is a_list
False
>>> a_copy_of_a_list == a_list
True

Shallow Copies

Shallow copies are just copies of the outermost container.

list.copy is a shallow copy:

>>> list_of_dict_of_set = [{'foo': set('abc')}]
>>> lodos_copy = list_of_dict_of_set.copy()
>>> lodos_copy[0]['foo'].pop()
'c'
>>> lodos_copy
[{'foo': {'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]

You don't get a copy of the interior objects. They're the same object - so when they're mutated, the change shows up in both containers.

Deep copies

Deep copies are recursive copies of each interior object.

>>> lodos_deep_copy = copy.deepcopy(list_of_dict_of_set)
>>> lodos_deep_copy[0]['foo'].add('c')
>>> lodos_deep_copy
[{'foo': {'c', 'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]

Changes are not reflected in the original, only in the copy.

Immutable objects

Immutable objects do not usually need to be copied. In fact, if you try to, Python will just give you the original object:

>>> a_tuple = tuple('abc')
>>> tuple_copy_attempt = a_tuple.copy()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'copy'

Tuples don't even have a copy method, so let's try it with a slice:

>>> tuple_copy_attempt = a_tuple[:]

But we see it's the same object:

>>> tuple_copy_attempt is a_tuple
True

Similarly for strings:

>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True

and for frozensets, even though they have a copy method:

>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True

When to copy immutable objects

Immutable objects should be copied if you need a mutable interior object copied.

>>> tuple_of_list = [],
>>> copy_of_tuple_of_list = tuple_of_list[:]
>>> copy_of_tuple_of_list[0].append('a')
>>> copy_of_tuple_of_list
(['a'],)
>>> tuple_of_list
(['a'],)
>>> deepcopy_of_tuple_of_list = copy.deepcopy(tuple_of_list)
>>> deepcopy_of_tuple_of_list[0].append('b')
>>> deepcopy_of_tuple_of_list
(['a', 'b'],)
>>> tuple_of_list
(['a'],)

As we can see, when the interior object of the copy is mutated, the original does not change.

Custom Objects

Custom objects usually store data in a __dict__ attribute or in __slots__ (a tuple-like memory structure.)

To make a copyable object, define __copy__ (for shallow copies) and/or __deepcopy__ (for deep copies).

from copy import copy, deepcopy

class Copyable:
    __slots__ = 'a', '__dict__'
    def __init__(self, a, b):
        self.a, self.b = a, b
    def __copy__(self):
        return type(self)(self.a, self.b)
    def __deepcopy__(self, memo): # memo is a dict of id's to copies
        id_self = id(self)        # memoization avoids unnecesary recursion
        _copy = memo.get(id_self)
        if _copy is None:
            _copy = type(self)(
                deepcopy(self.a, memo), 
                deepcopy(self.b, memo))
            memo[id_self] = _copy 
        return _copy

Note that deepcopy keeps a memoization dictionary of id(original) (or identity numbers) to copies. To enjoy good behavior with recursive data structures, make sure you haven't already made a copy, and if you have, return that.

So let's make an object:

>>> c1 = Copyable(1, [2])

And copy makes a shallow copy:

>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]

And deepcopy now makes a deep copy:

>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]

Solution 3

Shallow copy with copy.copy()

#!/usr/bin/env python3

import copy

class C():
    def __init__(self):
        self.x = [1]
        self.y = [2]

# It copies.
c = C()
d = copy.copy(c)
d.x = [3]
assert c.x == [1]
assert d.x == [3]

# It's shallow.
c = C()
d = copy.copy(c)
d.x[0] = 3
assert c.x == [3]
assert d.x == [3]

Deep copy with copy.deepcopy()

#!/usr/bin/env python3
import copy
class C():
    def __init__(self):
        self.x = [1]
        self.y = [2]
c = C()
d = copy.deepcopy(c)
d.x[0] = 3
assert c.x == [1]
assert d.x == [3]

Documentation: https://docs.python.org/3/library/copy.html

Tested on Python 3.6.5.

Share:
275,675

Related videos on Youtube

Roman
Author by

Roman

Updated on March 27, 2020

Comments

  • Roman
    Roman about 4 years

    I would like to create a copy of an object. I want the new object to possess all properties of the old object (values of the fields). But I want to have independent objects. So, if I change values of the fields of the new object, the old object should not be affected by that.

  • Jared Smith
    Jared Smith about 6 years
    That's neat and all, but does not answer the question as your copy function fails for custom classes and the question was about objects.
  • Alexey
    Alexey about 6 years
    @JaredSmith, it was not stated that the question was about all objects. It was not even clear if it was about deep or shallow copy (i would assume usual shallow one, but the accepted answer is about deep one). As to custom classes, if they are yours, you may just respect this kind of convention in their __init__ method. So, i thought this method may be good enough for certain purposes. In any case, I will be interested in informative comments on this suggestion.
  • Jared Smith
    Jared Smith about 6 years
    Consider class Foo(object): def __init__(self, arg): super(Foo, self).__init__() self.arg = arg Basic as it gets. If I do foo = Foo(3) bar = copy(foo) print(foo.arg) # 3 print(bar.arg) # <__main__.Foo object at ...> Meaning that your copy function is broken for even the most basic of classes. Again, it's a neat trick (hence no DV), but not an answer.
  • Alexey
    Alexey about 6 years
    @JaredSmith, i saw that there is copy.copy method for making shallow copies, but, maybe naively, it seems to me that it should be the responsibility of the class to provide a "shallow copy constructor." In such case why not to provide the same kinf of interface to it as dict and list do? So, if your class wants to take responsibility for copying its objects, why not to add a if isinstance(arg, type(self)) clause into __init__?
  • Jared Smith
    Jared Smith about 6 years
    Because you don't always have control over the classes you use the way you do ones you define. They may, just as one example, be C programs that have Python bindings (e.g. GTK, openalpr, parts of core). Not to mention that even if you took a third party library and added copy methods to every class, how are you going to weave that into your dependency management?
  • Russia Must Remove Putin
    Russia Must Remove Putin over 5 years
    This answer was flagged as "Not an answer", deleted, and undeleted - meta discussion here: meta.stackoverflow.com/questions/377844/…
  • Sven Marnach
    Sven Marnach over 5 years
    @AaronHall Thanks for letting me know! This certainly isn't the greatest answer I wrote, but I kind of agree with the decision that it should not be forcibly deleted. I'll brush it up a bit, but since there are already answers with all the details (notably yours), I'll keep it short.
  • DylanYoung
    DylanYoung over 4 years
    copy constructors are awesomesauce, but unless they're enforced by the language, they certainly aren't an answer to the question "How can I copy an object in this language?" Chainable method calls (methods that always return self even on mutation) are also awesomesauce, but a feature than Python has explicitly rejected. Not every language meets every need or desire :)