two Difference between a= b and a=ab in Python
set difference python (3)
I have recently applied
this
solution for averaging every N rows of matrix.
Although the solution works in general I had problems when applied to a 7x1 array. I have noticed that the problem is when using the
=
operator.
To make a small example:
import numpy as np
a = np.array([1,2,3])
b = np.copy(a)
a[1:] = a[:1]
b[1:] = b[1:]  b[:1]
print a
print b
which outputs:
[1 1 2]
[1 1 1]
So, in the case of an array
a = b
produces a different result than
a = a  b
. I thought until now that these two ways are exactly the same. What is the difference?
How come the method I am mentioning for summing every N rows in a matrix is working e.g. for a 7x4 matrix but not for a 7x1 array?
The docs say :
The idea behind augmented assignment in Python is that it isn't just an easier way to write the common practice of storing the result of a binary operation in its lefthand operand, but also a way for the lefthand operand in question to know that it should operate `on itself', rather than creating a modified copy of itself.
As a thumb rule, augmented substraction (
x=y
) is
x.__isub__(y)
, for
IN
place operation
IF
possible, when normal substraction (
x = xy
) is
x=x.__sub__(y)
. On non mutable objects like integers it's equivalent. But for mutable ones like arrays or lists, as in your example, they can be very different things.
Note: using inplace operations on NumPy arrays that share memory in no longer a problem in version 1.13.0 onward (see details here ). The two operation will produce the same result. This answer only applies to earlier versions of NumPy.
Mutating arrays while they're being used in computations can lead to unexpected results!
In the example in the question, subtraction with
=
modifies the second element of
a
and then immediately uses that
modified
second element in the operation on the third element of
a
.
Here is what happens with
a[1:] = a[:1]
step by step:

a
is the array with the data[1, 2, 3]
. 
We have two views onto this data:
a[1:]
is[2, 3]
, anda[:1]
is[1, 2]
. 
The inplace subtraction
=
begins. The first element ofa[:1]
, 1, is subtracted from the first element ofa[1:]
. This has modifieda
to be[1, 1, 3]
. Now we have thata[1:]
is a view of the data[1, 3]
, anda[:1]
is a view of the data[1, 1]
(the second element of arraya
has been changed). 
a[:1]
is now[1, 1]
and NumPy must now subtract its second element which is 1 (not 2 anymore!) from the second element ofa[1:]
. This makesa[1:]
a view of the values[1, 2]
. 
a
is now an array with the values[1, 1, 2]
.
b[1:] = b[1:]  b[:1]
does not have this problem because
b[1:]  b[:1]
creates a
new
array first and then assigns the values in this array to
b[1:]
. It does not modify
b
itself during the subtraction, so the views
b[1:]
and
b[:1]
do not change.
The general advice is to avoid modifying one view inplace with another if they overlap. This includes the operators
=
,
*=
, etc. and using the
out
parameter in universal functions (like
np.subtract
and
np.multiply
) to write back to one of the arrays.
Internally, the difference is that this:
a[1:] = a[:1]
is equivalent to this:
a[1:] = a[1:].__isub__(a[:1])
a.__setitem__(slice(1, None, None), a.__getitem__(slice(1, None, None)).__isub__(a.__getitem__(slice(1, None, None)))
while this:
b[1:] = b[1:]  b[:1]
maps to this:
b[1:] = b[1:].__sub__(b[:1])
b.__setitem__(slice(1, None, None), b.__getitem__(slice(1, None, None)).__sub__(b.__getitem__(slice(1, None, None)))
In some cases,
__sub__()
and
__isub__()
work in a similar way. But mutable objects should mutate and return themselves when using
__isub__()
, while they should return a new object with
__sub__()
.
Applying slice operations on numpy objects creates views on them, so using them directly accesses the memory of the "original" object.