Why NumPy 2 prints np.float64(1.5), and nine other answers that changed

I write practice questions for NumPy, and every snippet has to run before its answer is kept. Moving the bank to NumPy 2.4.4 broke more of my answers than I expected. A few changes raise an error, which is the easy case. Two of them quietly give you a different number.

Each snippet below was run in a fresh interpreter on NumPy 2.4.4. This article was written with the assistance of an AI writing program, and every output in it comes from actually running the code.

1. Scalars print with their type

a = np.array([1.5, 2.5])
print([x for x in a])
print(a[0])
[np.float64(1.5), np.float64(2.5)]
1.5

print on a single scalar still shows 1.5. The type appears in the repr, which is what you see inside a list, a dict, a tuple or a notebook cell. Nothing is broken, but a doctest or a snapshot test that compares printed lists fails. Call .tolist() when you want plain Python numbers.

2. Reductions show it too

print(repr(np.array([1, 5, 3]).max()))
np.int64(5)

Same cause. max, sum and friends return NumPy scalars, and their repr now says so.

3. A Python int that does not fit raises

a = np.array([1], dtype=np.int8)
try:
a + 300
except OverflowError as e:
print(type(e).__name__)
print(e)
OverflowError
Python integer 300 out of bounds for int8

NumPy 1 looked at the value 300, decided it needed int16 and quietly widened the result. NumPy 2 keeps the array's dtype and refuses a Python integer that cannot fit in it.

4. A Python int that fits can still overflow

a = np.array([100], dtype=np.int8)
print(a + 100)
[-56]

This is the dangerous one. 100 fits in int8, so there is no error, the result stays int8, and 200 wraps around to -56 without a warning. If a small integer dtype came from reading a file, cast before doing arithmetic.

5. float32 stays float32

print((np.float32(3) + 3.0).dtype)
float32

Under NumPy 1 this was float64, because the Python float was treated as a float64. Now a Python float adopts the NumPy side's precision. Results computed in float32 can differ in the last digits from what the same code gave you before.

6. np.float_, np.NaN and np.Inf are gone

`np.float_` was removed in the NumPy 2.0 release. Use `np.float64` instead.
`np.NaN` was removed in the NumPy 2.0 release. Use `np.nan` instead.
`np.Inf` was removed in the NumPy 2.0 release. Use `np.inf` instead.

These are AttributeErrors, and the messages tell you the replacement, which is kind of them.

7. np.trapz no longer exists

print(hasattr(np, 'trapz'), np.trapezoid([1, 2, 3]))
False 4.0

It is np.trapezoid now, and plenty of older tutorials on numerical integration still use the removed name.

8. np.in1d no longer exists

print(hasattr(np, 'in1d'), np.isin([1, 2], [2]))
False [False  True]

Use np.isin, which also works on arrays of any shape.

9. copy=False means never copy

try:
np.array([1, 2], copy=False)
except ValueError as e:
print(type(e).__name__)
print(str(e).splitlines()[0])
ValueError
Unable to avoid copy while creating an array as requested.

In NumPy 1, copy=False meant copy only if needed. In NumPy 2 it means never copy, and building an array from a list always needs a copy. np.asarray has the old behaviour.

10. tostring is gone

a = np.array([1], dtype=np.int16)
print(hasattr(a, 'tostring'), a.tobytes())
False b'\x01\x00'

tobytes does the same thing and has for years.

Where this came from

These are from a bank of 279 NumPy questions rebuilt on 2.4.4, where every option comes with the reason it is right or wrong. Twenty are free to play in the browser at thibaudlepan77-svg.github.io/interview-questions-verified, no account. The full set is a PDF on Ko-fi, and there is a data bundle with SQL, Python and pandas alongside. I also wrote up ten pandas answers that changed in pandas 3.

If one of these prints something different on your machine, tell me your version and the output. I would rather fix the question than defend it.

Did this save you a lookup? Buy me a coffee on Ko-fi.