Repository: numpy/numpy
Pull request: DOC: Update np.copy docstring to include ragged case
Fixes #15923

Problem statement:
BUG: np.copy does not work for nested np.arrays
I have a created a np.array which consists of two other np.array which do not have the same size. If I want to copy the array with np.copy, both arrays are still linked together. By the use of copy.deepcopy() there is no such issue. 

### Reproducing code example:

```python
import numpy as np 
import copy as cp 

a = np.array([1, 2, 3])
b = np.array([[3, 4, 5], [2, 4, 7]])
c = np.array([a, b])
d = np.copy(c)
e = cp.deepcopy(c)
d[1][0, 2] = 10
e[1][0, 1] = 20
print('c = ', c)
print('d = ', d)
print('e = ', e)

```
Output:
```python
c =  [array([1, 2, 3]) array([[ 3,  4, 10],
       [ 2,  4,  7]])]
d =  [array([1, 2, 3]) array([[ 3,  4, 10],
       [ 2,  4,  7]])]
e =  [array([1, 2, 3]) array([[ 3, 20,  5],
       [ 2,  4,  7]])]
```

### Numpy/Python version information:

numpy version: 1.15.4
python version: 3.6.8 |Anaconda, Inc.| (default, Dec 30 2018, 18:50:55) [MSC v.1915 64 bit (AMD64)]

Hints from issue discussion:
This is by design, `np.copy()` does not attempt to copy elements in object arrays.

A note in the docs for `np.copy` that this does not perform a deep-copy of object arrays would probably be a nice idea.
I think this only happens with ragged sequences. See:

```python
In [149]: a = np.array([1, 2, 3])

In [150]: b = np.array([1, 2, 3])

In [151]: c = np.copy(np.array([a, b], dtype=object))

In [152]: d = np.copy(np.array([a, [a, a]], dtype=object))

In [153]: c[0][0] = 123

In [154]: d[0][2] = 789

In [155]: a
Out[155]: array([  1,   2, 789])

In [156]: b
Out[156]: array([1, 2, 3])

In [157]: c
Out[157]:
array([[123, 2, 3],
       [1, 2, 3]], dtype=object)

In [158]: d
Out[158]:
array([array([  1,   2, 789]),
       list([array([  1,   2, 789]), array([  1,   2, 789])])],
      dtype=object)
```

Commit message: DOC: Update np.copy docstring to include ragged case

Fixes #15923

Patch to review:
diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py
index bfcf0d316653..71cf6d30f8ff 100644
--- a/numpy/lib/function_base.py
+++ b/numpy/lib/function_base.py
@@ -764,6 +764,19 @@ def copy(a, order='K', subok=False):
     >>> x[0] == z[0]
     False
 
+    Note that, NumPy will not copy ragged object arrays:
+
+    >>> a = np.array([1, 2, 3])
+    >>> b = np.copy(np.array([a, [a, a]], dtype=object))
+
+    Any updates on b changes a:
+
+    >>> b[0][0] = 10
+    >>> np.alltrue(b[0] == a)
+    True
+    >>> np.alltrue(b[1][0] == a)
+    True
+
     """
     return array(a, order=order, subok=subok, copy=True)
 


Code review comments:
diff_hunk: @@ -764,6 +764,19 @@ def copy(a, order='K', subok=False):
     >>> x[0] == z[0]
     False
 
+    Note that, NumPy will not copy ragged object arrays:
path: numpy/lib/function_base.py
text: @user1:
More accurately would be simply that
```suggestion
    Note that, NumPy will not copy the elements of object arrays:
```

You could see the behavior just as clearly with
```python
a = np.array([{}])
b = a.copy()
b[0]['value'] = 1
```

I don't think we should use "ragged arrays" as the example here, but a throwaway remark like "(including arrays within object arrays)" would be fine