6
>>> list1 = []
>>> list2 = list1
>>> list2 += [1]
>>> print list1
[1]

Comparing this to

>>> list1 = []
>>> list2 = list1
>>> list2 = list2 + [1]
>>> print list1
[]

Is there a reason why the '+='-operation modifies the original list?

EDIT: just to make my question a bit clearer

In most of the languages that I know the '+='-operator doesn't work this way and I would like to know why it was designed in this way in python.

some examples:

Ruby

irb(main):001:0> l = []
irb(main):002:0> a = l
irb(main):003:0> a += [2]
irb(main):004:0> l
=> []

Scala etc..

mkorpela
  • 4,317
  • 2
  • 19
  • 21
  • Interesting. Probably because in `list2 = list2 + [1]`, the `list2 + [1]` creates a new list, which is then assigned to `list2`. I suppose this is one of those "quirks" that pythonista's are used to. It is confusing for a beginner though. – Zabba Mar 09 '11 at 08:10
  • @MartijnPieters: I think the question is a bit different.. I'm asking here the reason for the design (as it is IMHO a bit odd compared to the way the same operator works for example in Scala and Ruby) and the other question is more about "What is happening?". – mkorpela Feb 07 '14 at 08:09
  • See the [original PEP (Python Enhancement Proposal](http://www.python.org/dev/peps/pep-0203/) for the motivations. *There are two main reasons for adding this feature to Python: simplicity of expression, and support for in-place operations. The end result is a tradeoff between simplicity of syntax and simplicity of expression; like most new features, augmented assignment doesn't add anything that was previously impossible. It merely makes these things easier to do.* – Martijn Pieters Feb 07 '14 at 11:25
  • @MartijnPieters: PEP tells about expensive matrix operations.. Anyway I'm not really sure anymore is my question about programming as I really just wanted to know why someone decided that it would be a good idea to do this in-place for lists. – mkorpela Feb 10 '14 at 11:21
  • 1
    It is a **whopping great idea** to do this in-place for lists. – Martijn Pieters Feb 10 '14 at 11:22
  • Ok. That is a matter of taste :P You can always use .append and .extend for in-place operations. And it violates the intuitive assumption that x = x + [1] is equal to x += [1].. And it is totally different from how it works in bunch of other languages. But yeah this discussion doesn't really belong to stack overflow. – mkorpela Feb 10 '14 at 11:25
  • No, I believe Ruby is the odd one in this case, and it's a reasonable expectation to have succinct syntax for both: modifying the collection and creating a new one. (The equivalence you mentioned, @mkorpela, is an example often encountered in materials about Python, ECMAScript at least to test or deepen the student's understanding of reference semantics and compound assignment.) I've just come here to SO after being surprised by Ruby's behaviour for this. – ByteEater Jul 09 '22 at 07:21
  • And in Scala it works like in Python too, see https://www.scala-lang.org/api/2.13.6/scala/collection/mutable/ListBuffer.html#addOne(elem:A):ListBuffer.this.type. On the other hand, C and C++ (https://en.cppreference.com/w/c/language/operator_assignment#Compound_assignment) do it the way you prefer. – ByteEater Jul 09 '22 at 07:24

7 Answers7

11

Lists in Python as stored by reference.

This means that when you do list2 = list1, you are not making a copy of the list - you are merely saying "list2 refers to the same thing list1 does," namely, the list you originally created when you did list1 = [].

Python specifies += to mean "append in place" for lists, because most of the time when you're using += on lists, that's what you want to do - you usually don't want to create new lists every single time you add an element.

Thus, when you append to list2, which "refers to the same object list1 does," and then read from list1, you see the appended item, as is expected since both of them point at the same list.

With +, however, a new list is always created because it doesn't make sense to modify either of the operands in place (since a+b doesn't imply the modification of a or b).

Therefore, when you do list2 = list2 + [1], you create a new list that has all of the contents of the original object pointed to by list2 and also 1, and then say that list2 now references that new list. Since it now references a different list than list1 does, when you go to read from list1 you still see the original list without the extra 1.

Amber
  • 507,862
  • 82
  • 626
  • 550
8

From the Python 2.6.4 documentation, Section 6.2.1. (Augmented assignment statements)

An augmented assignment expression like x += 1 can be rewritten as x = x + 1 to achieve a similar, but not exactly equal effect. In the augmented version, x is only evaluated once. Also, when possible, the actual operation is performed in-place, meaning that rather than creating a new object and assigning that to the target, the old object is modified instead.

[Emphasis added]

Oddthinking
  • 24,359
  • 19
  • 83
  • 121
  • 3
    just a little additional explanation about what's happening in the background: in fact, these two assignments don't use the same object's method, that's why the behaviour is different. The `x = x + 1` uses the `__add__()` method, which returns a new object, whereas `x += 1` uses the `__iadd__()` method, which modifies the object in-place. – mdeous Mar 09 '11 at 08:47
6

See in the documentation regarding Emulating numeric types, which describes the methods that implement this behaviour. This also applies to lists:

These methods are called to implement the augmented arithmetic assignments (+=, -=, *=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |=). These methods should attempt to do the operation in-place (modifying self) and return the result (which could be, but does not have to be, self). If a specific method is not defined, the augmented assignment falls back to the normal methods. For instance, to execute the statement x += y, where x is an instance of a class that has an __iadd__() method, x.__iadd__(y) is called. If x is an instance of a class that does not define a __iadd__() method, x.__add__(y) and y.__radd__(x) are considered, as with the evaluation of x + y.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
4

When you do list2 += [1] you are modifying the list in place. And that is why you don't change the reference the list points to, but you are changing the list directly. When you do a list2 = list2 + [1], you are creating a new list.

>>> l = []
>>> id(l)
41523720L
>>> l += [3]
>>> id(l)
41523720L     # same as l = []
>>> l = l+[3]
>>> id(l)
41532232L

That explains the difference.

user225312
  • 126,773
  • 69
  • 172
  • 181
2

Well, because that's how it works. When you write list2 = list2 + [1], you create new list and bind it to list2 name. When you use +=, operation happens "in place". So when list1 and list2 references the same object, which is case here, you modify it with += operator.

x13n
  • 4,103
  • 2
  • 21
  • 28
0

That's how += is supposed to work. In general,

a += b

means

a = a + b

But your particular scenario has a different problem. When you write

list2 = list1

no copy is made; list2 is now a reference to the very same list. Any modifications to list2 will be visible in list1 and vice versa.

In your second code snippet, list2 + [1] constructs a new list which is subsequently assigned to list2. Since this copy is independent of list1, no change to list1 is visible.

(Nitpicker's corner: Using operator overloading, it is possible to construct a class that behaves differently for +=. Don't. Just... don't.)

Thomas
  • 174,939
  • 50
  • 355
  • 478
0

In python, what you think of as variable names are more akin to pointers in most cases; "=" doesn't copy an object, it binds a new name to that object ("copy by reference" in other contexts). So list2 = list1 means that both of those names point to the same list, not just two copies of an identical list. Hence, "+=" modifies the single list which is pointed at by both names.

You can copy the list element-by-element (list2 = [i for in list1]) or by using the copy module (list2 = copy.copy(list1))

Andrew Jaffe
  • 26,554
  • 4
  • 50
  • 59