4

If I have this Python code:

foo = [3, 5, 9]
bar = foo
bar.append(9)
print(foo)

It returns

[3, 5, 9, 9]

Which surely means that when I appended 9 to bar, it also affected foo. How do I make the variable bar equal to foo, but so that when I edit bar, it does not effect foo?

LazySloth13
  • 2,369
  • 8
  • 28
  • 37

4 Answers4

6

You are altering a mutable value, so you need to make an explicit copy:

bar = list(foo)

or

bar = foo[:]

When assigning to a name in Python, all you do is store a reference to the value. Without creating a copy of the list, both foo and bar referred to the same list.

For non-mutable values this is not a problem; you replace a reference to, say, a string to point to a different value instead.

But lists (and dict and set and instances of most classes) are mutable. You didn't change foo, you changed the value foo refers to. You did foo.append(9), which Python sees as:

  • Find the value foo refers to.
  • Find the attribute .append on that value. This returns a method on a list instance.
  • Call the method, passing in the value 9. This alters the list.

Python names themselves are nothing more than labels, pointing to actual values. You could see the values as balloons, the names as little paper tags, and assignment is the act of tying the labels to the balloons. bar = foo created a second paper tag, that was tied to the exact same balloon.

See this older answer of mine where I push the balloon metaphor around some more.

Community
  • 1
  • 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • +1, Beat me to it. I'm personally more of a fan of using `list()`, as it works with any iterable and I feel it is more readable (which is backed up by the number of questions on SO asking what `[:]` does). – Gareth Latty May 03 '13 at 15:36
  • Why does this Python code on ideone: http://ideone.com/QIjOsD not produce the expected result (three 1's, not four)? Have I done something wrong? – LazySloth13 May 03 '13 at 15:51
  • 1
    @Lewis: nested lists. You only copied the *outer* list. The outer list is nothing but a dynamic series of paper tags, numbered sequentially. And to these numbered tags are more strings to more values, each another list. Copying the outer list just copied over the labels, including new strings to whatever they were tied to. Use the `copy` module to create a deep copy (`b = copy.deepcopy(a)`), or use a list comprehension: `b = [elem[:] for elem in a]`. The list comprehension creates shallow copies of each contained list, generating a new outer list with those shallow copies as elements. – Martijn Pieters May 03 '13 at 15:52
  • @MartijnPieters Sorry, I wrote the comment while you were editing yours :) – LazySloth13 May 03 '13 at 16:02
2

You make a copy:

bar = foo[:]  #copy of foo
bar.append(9)

This has to do with how python deals with assignment. Assignment says "take the object on the right and store it under the name on the left in the local namespace". So, in your case, you have foo and bar referencing the same object which is why appending to one also effects the other.

Things can get a little more interesting with immutable objects when using operators which usually operate on the object in place (e.g. +=). See my answer here for example and a lot more explanation.

Community
  • 1
  • 1
mgilson
  • 300,191
  • 65
  • 633
  • 696
2

When creating copies of the original while removing all references to it (so you can't edit the original), you use deep copy.

http://docs.python.org/2/library/copy.html

import copy
bar = copy.deepcopy(foo)
Austin Salgat
  • 416
  • 5
  • 13
2

Use deepcopy:

>>> from copy import deepcopy
>>> foo = [3, 5, 9]
>>> bar = deepcopy(foo)
>>> bar.append(9)
>>> print(foo)
[3, 5, 9]
>>> print bar
[3, 5, 9, 9]
E.Z.
  • 6,393
  • 11
  • 42
  • 69