Numpy: strange different behavior of inplace and explicit operation Numpy: strange different behavior of inplace and explicit operation arrays arrays

Numpy: strange different behavior of inplace and explicit operation


Ultimately this behavior comes down to free choices made by the developer(s), and so no good explanation need necessarily exist. However, I would like to defend/explain the observed behavior as follows.

In the case of

y = np.array(1.)y *= 1.

we create a np.ndarray object y, and then do an operation on it. Here, the most natural behavior is for the operation to (maybe) alter the value of y, while the type should stay the same. This is indeed what actually happens.

As an aside, note the distinction between type and NumPy data type (or dtype). Had we started out with y = np.array(1) (dtype of np.int64), the operation y *= 1. is now illegal as this would need to change the dtype in-place!

For the case x = 1.*np.array(1.), let's white it out as

x1 = 1.x2 = np.array(1.)x = x1*x2

Here, we do not create an object and then operate on it. Instead we create two objects, x1 and x2, and then combine them into a third object, x, using a symmetric operation (here binary multiplication). As x1 and x2 happen to have different (but compatible) types, the type of x is non-obvious: It could equally well be the type of x1 (float) or the type of x2 (numpy.ndarray). Surprisingly, the actual answer is neither, as the type of x is np.float64. This behavior stems from two separate choices.

Choice 1

Combining a 0-dimensional array with a scalar results in a scalar, not a 0-dimensional array. This is really the choice that trips you up. I suppose it might as well have been chosen the other way around. A global switch (e.g. np.return_scalar = False) would be a nice feature to have!

Choice 2

Combining NumPy numeric data types with standard Python numeric types results in NumPy numeric data types. Here, the first category include things like np.int64, np.float64, np.complex128 (and many more), while the latter consists only of int, float and complex (for Python 2, also long). Thus, float times np.float64 results in np.float64.

Taken together, the two choices indeed makes x = 1.*np.array(1.) a NumPy scalar of dtype np.float64.