16

In Python, is there any difference (semantics, efficiency, etc.) between writing x = x+1 and x += 1?

John
  • 4,596
  • 11
  • 37
  • 43

4 Answers4

17

Yes. Depending on how the class of x is coded, the short form has the option to modify x in-place, instead of creating a new object representing the sum and rebinding it back to the same name. This has an implication if you have multiple variables all referring to the same object - eg, with lists:

>>> a = b = []
>>> a += [5]
>>> a
[5]
>>> b
[5]
>>> a = a + [5]
>>> a
[5, 5]
>>> b
[5]

This happens because behind the scenes, the operators call different magic methods: + calls __add__ or __radd__ (which are expected not to modify either of their arguments) and += tries __iadd__ (which is allowed to modify self if it feels like it) before falling back to the + logic if __iadd__ isn't there.

user2357112
  • 260,549
  • 28
  • 431
  • 505
lvc
  • 34,233
  • 10
  • 73
  • 98
6

They are almost same for integers and floats, but for lists:

lis = lis+['foo'] creates a new list by concatenating lis and ['foo'] and then assigns the result to lis

and :

lis += [foo] is equivalent to lis.extend([foo])

>>> lis = [1,2,3]
>>> id(lis)
3078880140L
>>> lis += ['foo']   #or lis.extend(['foo'])
>>> id(lis)          #same object
3078880140L


>>> lis = [1,2,3]
>>> id(lis)
3078880076L
>>> lis = lis+['foo']
>>> id(lis)            #new object
3078880012L
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
4

Yes, they are different operators that compile to different bytecode:

>>> import dis
>>> def test1(x):
...    x = x + 1
... 
>>> def test2(x):
...    x += 1
... 
>>> dis.dis(test1)
  2           0 LOAD_FAST                0 (x) 
              3 LOAD_CONST               1 (1) 
              6 BINARY_ADD           
              7 STORE_FAST               0 (x) 
             10 LOAD_CONST               0 (None) 
             13 RETURN_VALUE         
>>> dis.dis(test2)
  2           0 LOAD_FAST                0 (x) 
              3 LOAD_CONST               1 (1) 
              6 INPLACE_ADD          
              7 STORE_FAST               0 (x) 
             10 LOAD_CONST               0 (None) 
             13 RETURN_VALUE         

In this case, it won't make a huge difference as ints are immutable. In theory they could be implemented in different ways (depending on the interpreter), but that won't change the way it operates on the value.

In general, they can be implemented to do completely different things (+ being implemented by the magic method __add__() and += with __iadd()__) - in most mutable containers, for example, it makes a huge difference, if you have different names referencing the same object:

>>> x = []
>>> y = x
>>> x += [1]
>>> y
[1]
>>> x = x + [1]
>>> y
[1]
>>> x
[1, 1]

You can see that when we assign x to y, they both point to the same list. When we use +=, we extend the list and both change. When we assign a new value to x, y still points to the original and remains unchanged.

Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
2

They are different because there are seperate operators for + and +=. With x = x + 1, the interpreter will treat it like x = x.__add__(1), while x += 1 will be x = x.__iadd(1), which can be much more efficient because it doesn't necessarily need to make a copy of x.

Brendan Long
  • 53,280
  • 21
  • 146
  • 188
  • `x += 1` actually becomes `x = x.__iadd__(1)`, and not just `x.__iadd__(1)` - the `i*` magic methods are still expected to return their result, even if that is `self` (importantly, per the documentation you linked, it *doesn't have to be* self). See also the disassembly of them in Lattyware's answer - both versions have a `STORE_FAST`. – lvc Oct 15 '12 at 23:48