Skip to content

Commit b43b95d

Browse files
authored
BUG: copy np.ndarray inputs to Index constructor by default (pandas-dev#63370)
1 parent 73d1165 commit b43b95d

File tree

2 files changed

+42
-5
lines changed

2 files changed

+42
-5
lines changed

pandas/core/indexes/base.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -341,8 +341,13 @@ class Index(IndexOpsMixin, PandasObject):
341341
Data type for the output Index. If not specified, this will be
342342
inferred from `data`.
343343
See the :ref:`user guide <basics.dtypes>` for more usages.
344-
copy : bool, default False
345-
Copy input data.
344+
copy : bool, default None
345+
Whether to copy input data, only relevant for array, Series, and Index
346+
inputs (for other input, e.g. a list, a new array is created anyway).
347+
Defaults to True for array input and False for Index/Series.
348+
Set to False to avoid copying array input at your own risk (if you
349+
know the input data won't be modified elsewhere).
350+
Set to True to force copying Series/Index input up front.
346351
name : object
347352
Name to be stored in the index.
348353
tupleize_cols : bool (default: True)
@@ -482,7 +487,7 @@ def __new__(
482487
cls,
483488
data=None,
484489
dtype=None,
485-
copy: bool = False,
490+
copy: bool | None = None,
486491
name=None,
487492
tupleize_cols: bool = True,
488493
) -> Self:
@@ -499,9 +504,16 @@ def __new__(
499504
if not copy and isinstance(data, (ABCSeries, Index)):
500505
refs = data._references
501506

507+
if isinstance(data, (ExtensionArray, np.ndarray)):
508+
# GH 63306
509+
if copy is not False:
510+
if dtype is None or astype_is_view(data.dtype, dtype):
511+
data = data.copy()
512+
copy = False
513+
502514
# range
503515
if isinstance(data, (range, RangeIndex)):
504-
result = RangeIndex(start=data, copy=copy, name=name)
516+
result = RangeIndex(start=data, copy=bool(copy), name=name)
505517
if dtype is not None:
506518
return result.astype(dtype, copy=False)
507519
# error: Incompatible return value type (got "MultiIndex",
@@ -569,7 +581,7 @@ def __new__(
569581
data = com.asarray_tuplesafe(data, dtype=_dtype_obj)
570582

571583
try:
572-
arr = sanitize_array(data, None, dtype=dtype, copy=copy)
584+
arr = sanitize_array(data, None, dtype=dtype, copy=bool(copy))
573585
except ValueError as err:
574586
if "index must be specified when data is not list-like" in str(err):
575587
raise cls._raise_scalar_data_error(data) from err

pandas/tests/copy_view/index/test_index.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
DataFrame,
66
Index,
77
Series,
8+
array,
89
)
910
import pandas._testing as tm
1011
from pandas.tests.copy_view.util import get_array
@@ -150,3 +151,27 @@ def test_index_values():
150151
idx = Index([1, 2, 3])
151152
result = idx.values
152153
assert result.flags.writeable is False
154+
155+
156+
def test_constructor_copy_input_ndarray_default():
157+
arr = np.array([0, 1])
158+
idx = Index(arr)
159+
assert not np.shares_memory(arr, get_array(idx))
160+
161+
162+
def test_constructor_copy_input_ea_default():
163+
arr = array([0, 1], dtype="Int64")
164+
idx = Index(arr)
165+
assert not tm.shares_memory(arr, idx.array)
166+
167+
168+
def test_series_from_temporary_index_readonly_data():
169+
# GH 63370
170+
arr = np.array([0, 1], dtype=np.dtype(np.int8))
171+
arr.flags.writeable = False
172+
ser = Series(Index(arr))
173+
assert not np.shares_memory(arr, get_array(ser))
174+
assert ser._mgr._has_no_reference(0)
175+
ser[[False, True]] = np.array([0, 2], dtype=np.dtype(np.int8))
176+
expected = Series([0, 2], dtype=np.dtype(np.int8))
177+
tm.assert_series_equal(ser, expected)

0 commit comments

Comments
 (0)