Assignment statements in Python do not copy objects, they create bindings between a target and an object. For collections that are mutable or contain mutable items, a copy is sometimes needed so one can change one copy without changing the other.
Consider the following program fragment:
a=[1,'2',[3]]
b=a[:]
print [id(x) for x in a,b] #[140550956421992, 140550956422136]
print [id(x) for x in a] #[11108696, 140551018996080, 140550956422280]
print [id(x) for x in b] #[11108696, 140551018996080, 140550956422280]
We can see that a
and b
is not the same object but all the elements they have are the same. Because slice copy is a shadow copy which returns a new object but does not create new objects for its elements.
When we make changes to the list in b, the corresponding list in a also changes:
b[0]+=111
b[1]+='222'
b[2]+=[3,3,3]
print b #[112, '2222', [3, 3, 3, 3]]
print a #[1, '2', [3, 3, 3, 3]]
print [id(x) for x in a] #[11108696, 140551018996080, 140550956422280]
print [id(x) for x in b] #[11110016, 140550956333660, 140550956422280]
Integer and String are not mutable objects, when we use +=
operator to modify them, they just return a new object. So the address has changed.
But list is mutable, when appending a new item to it, changes are made internally which will not create a new object. So, when b changes, a changes in the meantime.
Sometimes we don't want to a
changes also, so we should make a deep copy from a
. In the module copy, it provides two factory method:copy
and deepcopy
. copy
is a shadow copy version which is the same as slice copy in list
. And deepcopy
is a version which creates a new object when copy an mutable object.
import copy
a=[1,2,[3]]
b=copy.deepcopy(a)
print a #[1, 2, [3]]
print b #[1, 2, [3]]
print [id(x) for x in a,b] #[140550956422496, 140550956422136]
print [id(x) for x in a] #[11108696, 11108672, 140551018855528]
print [id(x) for x in b] #[11108696, 11108672, 140550956421344]
When we change the list in b, a does not change:
b[2]+=[22,22,22]
print b #[1, 2, [3, 22, 22, 22]]
print a #[1, 2, [3]]
Differences are only relative to mutable objects when coping elements from compound objects . When we exchange list
with tuple
, deepcopy
makes the same result as copy
a=[1,(2,)]
b=copy.copy(a)
c=copy.deepcopy(a)
print [id(x) for x in a] #[11108696, 140551018808080]
print [id(x) for x in b] #[11108696, 140551018808080]
print [id(x) for x in c] #[11108696, 140551018808080]
When a
is a tuple
:
a=(1,[2])
b=a
c=tuple(a)
d=copy.copy(a)
e=copy.deepcopy(a)
#[140306117732688, 140306117732688,140306117732688, 140306117732688,140306117729816]
print [id(x) for x in a,b,c,d,e]
print [id(x) for x in a] #[33198424, 140306117938064]
print [id(x) for x in b] #[33198424, 140306117938064]
print [id(x) for x in c] #[33198424, 140306117938064]
print [id(x) for x in d] #[33198424, 140306117938064]
print [id(x) for x in e] #[33198424, 140306117938424]
The Python Doc says that:
Two problems often exist with deep copy operations that don’t exist with shallow copy operations:
-
Recursive objects (compound objects that, directly or indirectly, contain a reference to themselves) may cause a recursive loop.
-
Because deep copy copies everything it may copy too much, e.g., administrative data structures that should be shared even between copies.
Examples lacking....