How to create a copy of an object in Python?

I would like to create a copy of the object. I want the new object to have all the properties of the old object (field values). But I want to have independent objects. Therefore, if I change the field values ​​of a new object, this object should not affect the old object.

+129
python object oop copy
Jan 25 2018-11-11T00:
source share
4 answers

To get a completely independent copy of an object, you can use copy.deepcopy() .

For more information on shallow and deep copying, please refer to the other answers to this question and a good explanation in this answer to the related question .

+111
Jan 25 2018-11-11T00:
source share

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): # memo is a dict of id 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 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] 
+32
Oct 25 '17 at 18:16
source share

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) dx = [3] assert cx == [1] assert dx == [3] # It shallow. c = C() d = copy.copy(c) dx[0] = 3 assert cx == [3] assert dx == [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) dx[0] = 3 assert cx == [1] assert dx == [3] 

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

Tested on Python 3.6.5.

+3
Sep 04 '18 at 6:29
source share

I believe the following should work with many classes in Python:

 def copy(obj): return type(obj)(obj) 

(Of course, I’m not talking about “deep copies” here, this is a separate story, and which may not be a very clear concept - how deep is deep enough?)

According to my tests with Python 3, for immutable objects, such as tuples or strings, it returns the same object (since there is no need to make a shallow copy of the immutable object), but for lists or dictionaries it creates an independent shallow copy.,

Of course, this method only works for classes whose constructors behave accordingly. Possible uses: making a shallow copy of the standard Python container class.

-one
Feb 21 '18 at 12:38
source share



All Articles