What happens when a variable is assigned to another variable in python? For example:
>>> x = 5 >>> y = x
Both x
and y
will have the value 5
. But, when x
was assigned to y
, y
was not created as a completely new/separate object. Instead, an alias for x
was created. That is, y
points to the memory location of x
. It does not have it's own memory location - yet.
>>> id(x) 140428600776960 >>> id(y) 140428600776960 >>> x is y True
You may never have any problems with this when working with immutable types because the alias is broken as soon as either of the two variables change.
>>> x += 2 >>> x 7 >>> y 5 >>> id(x) 140539682924864 >>> id(y) 140428600776960 >>> x is y False
But when working with mutable types, the alias is not broken when the original is updated. This means changes in x
would reflect in y
.
>>> x = [1,2,3] >>> y = x >>> x [1, 2, 3] >>> y [1, 2, 3] >>> x is y True >>> >>> x.append(4) >>> x.append(5) >>> x [1, 2, 3, 4, 5] >>> y [1, 2, 3, 4, 5] >>> x is y True
y
was updated externally. This might be a cause of bugs if for example a value is updated by an external library and other variables are affected.
This can be prevented by creating shallow/deep copies of objects instead of using assignment.
Shallow copy
A shallow copy creates a new object, then populates it with references of the objects in the original object. Continuing with the previous example, a shallow copy can be created using either the list()
or copy()
command.
>>> z = list(x) >>> z [1, 2, 3, 4, 5]
Now if some more values are appended to x
, y
will still be affected by z
will not.
>>> x.append(6) >>> x.append(7) >>> x [1, 2, 3, 4, 5, 6, 7] >>> y [1, 2, 3, 4, 5, 6, 7] >>> z [1, 2, 3, 4, 5]
However, a shallow copy doesn't fully solve the problem because even though a new list was created, the objects in the list are still references to the objects in x
.
As it is currently, updating x[0]
would not affect z[0]
because - immutable objects - the alias would be broken. But, if we were dealing with a list of lists, an update in x[0]
would affect z[0]
.
>>> x = [[1,2], [3,4]] >>> y = x >>> z = list(x) >>> x [[1, 2], [3, 4]] >>> y [[1, 2], [3, 4]] >>> z [[1, 2], [3, 4]] >>> x.append([5,6]) >>> x [[1, 2], [3, 4], [5, 6]] >>> y [[1, 2], [3, 4], [5, 6]] >>> z [[1, 2], [3, 4]] >>> >>> x[0][1] = 'edited' >>> x [[1, 'edited'], [3, 4], [5, 6]] >>> y [[1, 'edited'], [3, 4], [5, 6]] >>> z [[1, 'edited'], [3, 4]]
Deep copy
A deep copy creates a new object, and completely new instances of the objects in it. That is, a deep copied object is completely independent of the original. Updating objects in the original would not affect the deep copied object since there's no longer any connection.
>>> import copy >>> x = [[1,2], [3,4]] >>> z = copy.deepcopy(x) >>> x [[1, 2], [3, 4]] >>> z [[1, 2], [3, 4]] >>> x is z False >>> x[0][1] = 'edited' >>> x [[1, 'edited'], [3, 4]] >>> z [[1, 2], [3, 4]]
Top comments (2)
excellent write up, thanks for this article
very nice! thx