How can I create a copy of an object in Python?
Thus, if I change the field values of the new object, this should not affect the old object.
You mean a volatile object then.
In Python 3, lists get the copy method (in 2 you would use a snippet to create 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
Small copies
Small copies are just copies of the outermost container.
list.copy is a shallow copy:
>>> list_of_dict_of_set = [{'foo': set('abc')} ... ] >>> 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 do not receive a copy of the interior. This is the same object - therefore, when they are modified, the change is detected in both containers.
Deep copies
Deep copies are recursive copies of each piece of furniture.
>>> 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 a copy.
Fixed Items
Immutable objects usually do not need to be copied. In fact, if you try, Python will simply 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 do not even have a copy method, so let's try it with a fragment:
>>> tuple_copy_attempt = a_tuple[:]
But we see 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, although 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 must be copied if you need to copy a mutable internal object.
>>> 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 see, when the internal object of the copy is modified, the original does not change.
Custom objects
Custom objects typically store data in __dict__ or __slots__ (a memory structure similar to a tuple).
To create a __copy__ object, define __copy__ (for small 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):
Note that deepcopy stores a dictionary for deepcopy id(original) (or ID numbers) to copy. To get good behavior with recursive data structures, make sure you haven't made a copy yet, and if you have one, return it.
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]