What is the difference between i = i + 1 and i += 1 in a 'for' loop?

216,219

Solution 1

The difference is that one modifies the data-structure itself (in-place operation) b += 1 while the other just reassigns the variable a = a + 1.


Just for completeness:

x += y is not always doing an in-place operation, there are (at least) three exceptions:

  • If x doesn't implement an __iadd__ method then the x += y statement is just a shorthand for x = x + y. This would be the case if x was something like an int.

  • If __iadd__ returns NotImplemented, Python falls back to x = x + y.

  • The __iadd__ method could theoretically be implemented to not work in place. It'd be really weird to do that, though.

As it happens your bs are numpy.ndarrays which implements __iadd__ and return itself so your second loop modifies the original array in-place.

You can read more on this in the Python documentation of "Emulating Numeric Types".

These [__i*__] 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, if x is an instance of a class with an __iadd__() method, x += y is equivalent to x = x.__iadd__(y) . Otherwise, x.__add__(y) and y.__radd__(x) are considered, as with the evaluation of x + y. In certain situations, augmented assignment can result in unexpected errors (see Why does a_tuple[i] += ["item"] raise an exception when the addition works?), but this behavior is in fact part of the data model.

Solution 2

In the first example, you are reassigning the variable a, while in the second one you are modifying the data in-place, using the += operator.

See the section about 7.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.

+= operator calls __iadd__. This function makes the change in-place, and only after its execution, the result is set back to the object you are "applying" the += on.

__add__ on the other hand takes the parameters and returns their sum (without modifying them).

Solution 3

As already pointed out, b += 1 updates b in-place, while a = a + 1 computes a + 1 and then assigns the name a to the result (now a does not refer to a row of A anymore).

To understand the += operator properly though, we need also to understand the concept of mutable versus immutable objects. Consider what happens when we leave out the .reshape:

C = np.arange(12)
for c in C:
    c += 1
print(C)  # [ 0  1  2  3  4  5  6  7  8  9 10 11]

We see that C is not updated, meaning that c += 1 and c = c + 1 are equivalent. This is because now C is a 1D array (C.ndim == 1), and so when iterating over C, each integer element is pulled out and assigned to c.

Now in Python, integers are immutable, meaning that in-place updates are not allowed, effectively transforming c += 1 into c = c + 1, where c now refers to a new integer, not coupled to C in any way. When you loop over the reshaped arrays, whole rows (np.ndarray's) are assigned to b (and a) at a time, which are mutable objects, meaning that you are allowed to stick in new integers at will, which happens when you do a += 1.

It should be mentioned that though + and += are meant to be related as described above (and very much usually are), any type can implement them any way it wants by defining the __add__ and __iadd__ methods, respectively.

Solution 4

The short form(a += 1) has the option to modify a in-place , instead of creating a new object representing the sum and rebinding it back to the same name(a = a + 1).So,The short form(a += 1) is much efficient as it doesn't necessarily need to make a copy of a unlike a = a + 1.

Also even if they are outputting the same result, notice they are different because they are separate operators: + and +=

Solution 5

First off: The variables a and b in the loops refer to numpy.ndarray objects.

In the first loop, a = a + 1 is evaluated as follows: the __add__(self, other) function of numpy.ndarray is called. This creates a new object and hence, A is not modified. Afterwards, the variable a is set to refer to the result.

In the second loop, no new object is created. The statement b += 1 calls the __iadd__(self, other) function of numpy.ndarray which modifies the ndarray object in place to which b is referring to. Hence, B is modified.

Share:
216,219
Dammi
Author by

Dammi

Updated on August 06, 2022

Comments

  • Dammi
    Dammi over 1 year

    I found out a curious thing today and was wondering if somebody could shed some light into what the difference is here?

    import numpy as np
    
    A = np.arange(12).reshape(4,3)
    for a in A:
        a = a + 1
    
    B = np.arange(12).reshape(4,3)
    for b in B:
        b += 1
    

    After running each for loop, A has not changed, but B has had one added to each element. I actually use the B version to write to a initialized NumPy array within a for loop.

  • Daniel F
    Daniel F over 7 years
    Piggybacking the top comment for a less technical explanation: a=a+1 here is functionally equivalent to dummy=a+1, as the a on left left side of the equals sign is being reassigned to no longer be a view of A. Since += doesn't reassign like = does, but instead modifies 'in place,' b remains a view of B and thus B increments where A doesn't.
  • nevsan
    nevsan over 7 years
    There is a very important consideration to make especially when using numpy. When using in place add, the data type of the original variable is retained. So if you, for example, attempt to add a complex value to a real valued numpy array, the in place add will remain real valued, whereas if you use reassignment, you get the correct complex result. Be careful when using in place adds with numpy.
  • MSeifert
    MSeifert over 7 years
    @nevsan In that case a TypeError is raised so while it may not work as intended it doesn't fail silently or do the wrong thing as your comment suggests.
  • Dammi
    Dammi over 7 years
    Thanks! To clarify, I did realize that i += 1 and i = i + 1 was not the same thing, but since I was using Numpy, I thought that i = i + 1 still meant I was writing to the same memory of i in A.
  • MSeifert
    MSeifert over 7 years
    @Dammi Numpy can't change the way names are linked to objects, that's entirely determined by python itself. But to explicitly write into the same memory you could use i[:] = i+1 (but it's generally better to use += or the out-argument of several ufuncs).