How can I create a copy of an object in Python?
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.
Related videos on Youtube
Roman
Updated on March 27, 2020Comments
-
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 about 6 yearsThat'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 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 about 6 yearsConsider
class Foo(object): def __init__(self, arg): super(Foo, self).__init__() self.arg = arg
Basic as it gets. If I dofoo = Foo(3) bar = copy(foo) print(foo.arg) # 3 print(bar.arg) # <__main__.Foo object at ...>
Meaning that yourcopy
function is broken for even the most basic of classes. Again, it's a neat trick (hence no DV), but not an answer. -
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 asdict
andlist
do? So, if your class wants to take responsibility for copying its objects, why not to add aif isinstance(arg, type(self))
clause into__init__
? -
Jared Smith about 6 yearsBecause 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 over 5 yearsThis answer was flagged as "Not an answer", deleted, and undeleted - meta discussion here: meta.stackoverflow.com/questions/377844/…
-
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 over 4 yearscopy 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 :)