np.array copy=False vs copy=None: portable "copy only if needed" across NumPy 1.x and 2.x
0 reputation · 29 Aug 2024, 13:25 UTC
0 reputation · 29 Aug 2024, 13:25 UTC
I maintain a library that wraps user inputs with np.array and must avoid unnecessary copies of large ndarrays while still guaranteeing an array result. The code has to run against both a NumPy 1.x line and NumPy 2.x during a transition period, so I need one spelling of "copy only if a copy is actually required" that behaves predictably on both.
My understanding of the documented behavior is that basic slicing returns a view sharing memory with the base, while advanced indexing returns a copy, and that the copy keyword changed meaning in NumPy 2.x: copy=False now raises ValueError when a copy is unavoidable, while copy=None was introduced to mean "copy only if needed", matching np.asarray. What is unclear to me is the contract on the 1.x side: copy=None does not appear to be part of the documented 1.x API, so I do not know whether passing it is safe, ignored, or version-dependent there.
I also cannot assume the input is already an ndarray; it may be a list, a nested sequence, or an array of a different dtype, so the "unavoidable copy" path will be exercised regularly.
copy=None accepted and meaningful on NumPy 1.x releases, or is it strictly a 2.x addition that 1.x code must never pass?numpy.__version__ the accepted approach, or is there a single call (for example np.asarray) with identical copy semantics on both lines?np.asarray on 1.x guarantee the same "copy only if needed" behavior that 2.x documents for copy=None, including for dtype-mismatched inputs?To achieve "copy only if needed" behavior that is portable across both NumPy 1.x and 2.x, use np.asarray(). This function is the stable, cross-version standard for ensuring an input is an ndarray while avoiding a copy if the input is already an array of the requested dtype.
The copy parameter in np.array() underwent a significant semantic shift in NumPy 2.0. The following table outlines the differences in how these calls handle inputs that require a copy (e.g., a Python list or a dtype mismatch).
| Call | NumPy 1.x Behavior | NumPy 2.x Behavior |
|---|---|---|
np.asarray(x) |
Copies only if needed | Copies only if needed |
np.array(x, copy=False) |
Copies only if needed | Raises ValueError if copy is required |
np.array(x, copy=None) |
TypeError (invalid arg) | Copies only if needed |
np.asarray is the correct choicenp.asarray has maintained the "copy only if needed" contract across both major versions.np.array(copy=False) in 2.x, it will not crash when encountering a Python list or a different dtype; it will simply perform the necessary copy to satisfy the array requirement.np.asarray removes the need to check numpy.__version__, reducing maintenance overhead during the transition.You can verify this behavior in your environment with the following scoped tests:
import numpy as np
# Case 1: Already an array (Should NOT copy)
base = np.array([1, 2, 3])
result = np.asarray(base)
print(f"No copy: {np.may_share_memory(base, result)}")
# Case 2: Python list (MUST copy)
result_list = np.asarray([1, 2, 3])
# This succeeds in both 1.x and 2.x, whereas np.array(copy=False) fails in 2.x
Note on Dtype: If you specify a dtype in np.asarray(x, dtype=...), a copy will be forced if the input array's dtype does not match, regardless of the NumPy version.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.