diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index df898ccf47bfa..3239621468c5a 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -341,8 +341,13 @@ class Index(IndexOpsMixin, PandasObject): Data type for the output Index. If not specified, this will be inferred from `data`. See the :ref:`user guide ` for more usages. - copy : bool, default False - Copy input data. + copy : bool, default None + Whether to copy input data, only relevant for array, Series, and Index + inputs (for other input, e.g. a list, a new array is created anyway). + Defaults to True for array input and False for Index/Series. + Set to False to avoid copying array input at your own risk (if you + know the input data won't be modified elsewhere). + Set to True to force copying Series/Index input up front. name : object Name to be stored in the index. tupleize_cols : bool (default: True) @@ -482,7 +487,7 @@ def __new__( cls, data=None, dtype=None, - copy: bool = False, + copy: bool | None = None, name=None, tupleize_cols: bool = True, ) -> Self: @@ -499,9 +504,16 @@ def __new__( if not copy and isinstance(data, (ABCSeries, Index)): refs = data._references + if isinstance(data, (ExtensionArray, np.ndarray)): + # GH 63306 + if copy is not False: + if dtype is None or astype_is_view(data.dtype, dtype): + data = data.copy() + copy = False + # range if isinstance(data, (range, RangeIndex)): - result = RangeIndex(start=data, copy=copy, name=name) + result = RangeIndex(start=data, copy=bool(copy), name=name) if dtype is not None: return result.astype(dtype, copy=False) # error: Incompatible return value type (got "MultiIndex", @@ -569,7 +581,7 @@ def __new__( data = com.asarray_tuplesafe(data, dtype=_dtype_obj) try: - arr = sanitize_array(data, None, dtype=dtype, copy=copy) + arr = sanitize_array(data, None, dtype=dtype, copy=bool(copy)) except ValueError as err: if "index must be specified when data is not list-like" in str(err): raise cls._raise_scalar_data_error(data) from err diff --git a/pandas/tests/copy_view/index/test_index.py b/pandas/tests/copy_view/index/test_index.py index e51f5658cf437..aa594bd051a88 100644 --- a/pandas/tests/copy_view/index/test_index.py +++ b/pandas/tests/copy_view/index/test_index.py @@ -5,6 +5,7 @@ DataFrame, Index, Series, + array, ) import pandas._testing as tm from pandas.tests.copy_view.util import get_array @@ -150,3 +151,27 @@ def test_index_values(): idx = Index([1, 2, 3]) result = idx.values assert result.flags.writeable is False + + +def test_constructor_copy_input_ndarray_default(): + arr = np.array([0, 1]) + idx = Index(arr) + assert not np.shares_memory(arr, get_array(idx)) + + +def test_constructor_copy_input_ea_default(): + arr = array([0, 1], dtype="Int64") + idx = Index(arr) + assert not tm.shares_memory(arr, idx.array) + + +def test_series_from_temporary_index_readonly_data(): + # GH 63370 + arr = np.array([0, 1], dtype=np.dtype(np.int8)) + arr.flags.writeable = False + ser = Series(Index(arr)) + assert not np.shares_memory(arr, get_array(ser)) + assert ser._mgr._has_no_reference(0) + ser[[False, True]] = np.array([0, 2], dtype=np.dtype(np.int8)) + expected = Series([0, 2], dtype=np.dtype(np.int8)) + tm.assert_series_equal(ser, expected)