Skip to content

Unclear documentation of np.copy #15570

@moble

Description

@moble

A very painful bug of mine turns out to originate in the different behaviors of np.copy(a) and a.copy() — specifically, the fact that the latter preserves subclass, whereas the former does not.

If I look through the actual code, it's clear why this happens, but it's certainly not clear from the documentation that it will happen. In fact, I consider the documentation for np.copy downright misleading because it addresses one difference between the two (namely, the default order argument), which lulled me into believing that it was the only difference. (And yes, the documentation technically refers to ndarray.copy, while my subclass was not ndarray, but I don't read documentation like a lawyer.)

I'll acknowledge that changing the default behavior is off the table, so I'll suggest two possibilities that would at least help avoid this situation:

  1. Add a subok=False option to np.copy that is just passed through to np.array. The default behavior will remain the same, but the presence of this argument will alert the user to the fact that there might be an issue.

  2. Add to the docstring an explicit statement of the fact that subclasses will not pass through.

@array_function_dispatch(_copy_dispatcher)
def copy(a, order='K'):
"""
Return an array copy of the given object.
Parameters
----------
a : array_like
Input data.
order : {'C', 'F', 'A', 'K'}, optional
Controls the memory layout of the copy. 'C' means C-order,
'F' means F-order, 'A' means 'F' if `a` is Fortran contiguous,
'C' otherwise. 'K' means match the layout of `a` as closely
as possible. (Note that this function and :meth:`ndarray.copy` are very
similar, but have different default values for their order=
arguments.)
Returns
-------
arr : ndarray
Array interpretation of `a`.
Notes
-----
This is equivalent to:
>>> np.array(a, copy=True) #doctest: +SKIP
Examples
--------
Create an array x, with a reference y and a copy z:
>>> x = np.array([1, 2, 3])
>>> y = x
>>> z = np.copy(x)
Note that, when we modify x, y changes, but not z:
>>> x[0] = 10
>>> x[0] == y[0]
True
>>> x[0] == z[0]
False
"""
return array(a, order=order, copy=True)

Metadata

Metadata

Assignees

No one assigned

    Labels

    triagedIssue/PR that was discussed in a triage meeting

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions