diff --git a/numpy/_core/src/multiarray/methods.c b/numpy/_core/src/multiarray/methods.c index 62cc25e64c1b..adc1e53e24ab 100644 --- a/numpy/_core/src/multiarray/methods.c +++ b/numpy/_core/src/multiarray/methods.c @@ -811,8 +811,8 @@ array_astype(PyArrayObject *self, /* * If the memory layout matches and, data types are equivalent, - * and it's not a subtype if subok is False, then we - * can skip the copy. + * it's not a subtype if subok is False, and if the cast says + * view are possible, we can skip the copy. */ if (forcecopy != NPY_AS_TYPE_COPY_ALWAYS && (order == NPY_KEEPORDER || @@ -823,11 +823,15 @@ array_astype(PyArrayObject *self, PyArray_IS_C_CONTIGUOUS(self)) || (order == NPY_FORTRANORDER && PyArray_IS_F_CONTIGUOUS(self))) && - (subok || PyArray_CheckExact(self)) && - PyArray_EquivTypes(dtype, PyArray_DESCR(self))) { - Py_DECREF(dtype); - Py_INCREF(self); - return (PyObject *)self; + (subok || PyArray_CheckExact(self))) { + npy_intp view_offset; + npy_intp is_safe = PyArray_SafeCast(dtype, PyArray_DESCR(self), + &view_offset, NPY_NO_CASTING, 1); + if (is_safe && (view_offset != NPY_MIN_INTP)) { + Py_DECREF(dtype); + Py_INCREF(self); + return (PyObject *)self; + } } if (!PyArray_CanCastArrayTo(self, dtype, casting)) { diff --git a/numpy/_core/tests/test_stringdtype.py b/numpy/_core/tests/test_stringdtype.py index 8af097c1a9d9..176516c71d18 100644 --- a/numpy/_core/tests/test_stringdtype.py +++ b/numpy/_core/tests/test_stringdtype.py @@ -497,6 +497,13 @@ def test_create_with_copy_none(string_list): assert arr_view is arr +def test_astype_copy_false(): + orig_dt = StringDType() + arr = np.array(["hello", "world"], dtype=StringDType()) + assert not arr.astype(StringDType(coerce=False), copy=False).dtype.coerce + + assert arr.astype(orig_dt, copy=False).dtype is orig_dt + @pytest.mark.parametrize( "strings", [