Repository: numpy/numpy
Pull request: DOC: add explanation of dtype to parameter values for np.append
Improve doc strings to include explanation of `dtype` to parameter `values` for `np.append`

closes https://github.com/numpy/numpy/issues/26291

Problem statement:
DOC: np.append docs should explain how appending different dtypes works
### Describe the issue:

When trying to append empty list to existing array, numpy array changes its dtype. This is absolutely not obvious behaviour and I've barely found this problem. 

It may sound like minor problem, but in my case it was a reason of not deleting about 50k images by its indices, when I had to.

### Reproduce the code example:

```python
a = np.array([1, 2], dtype=int)
b = []
np.append(a, b) # array([1., 2.])
```


### Error message:

_No response_

### Python and NumPy Versions:

Python 3.11.5
Numpy 1.26.4

### Runtime Environment:

_No response_

### Context for the issue:

_No response_

Hints from issue discussion:
It's intended behavior, but it should be documented better.
@rkern Can I work on a PR to document this behaviour? In that case can you assign this issue to me?
What is "intended" in changing dtype of array, which values are not changed¿
Both arguments are converted to `ndarray`s through the usual method of `np.asarray()`. The default dtype for empty lists is `float64`. Once the arguments are converted to `ndarray`s, then their dtypes are compared to figure out the common dtype they can both be safely coerced to; `int64` and `float64` can both go to `float64`.

```python
>>> np.asarray([]).dtype
dtype('float64')
```

If you want to work around this, even in the case of empty lists, be sure to coerce _both_ of your arguments to `ndarray`s explicitly with `dtype=int` ahead of time.

```python
>>> a = np.array([1, 2], dtype=int)
>>> b = np.array([], dtype=int)
>>> np.append(a, b)
array([1, 2])
```

For what it's worth, I would agree that for `append` casting to the first array would probably have been a nicer choice for NumPy, but that doesn't mean I think we should change it (at least not without a long warning which may be tedious).

There was discussion (maybe even a PR?) to add a `dtype=` argument, which would allow you to write `np.append(arr, ..., dtype=arr.dtype)`.
`np.concatenate` already has that option and is really basically what `np.append` uses internally.
That could be reactivated.

(For the empty list case, the `dtype=` argument may still be awkward due to unsafe casting of the "inferred" float64.)

---

Improving the docs is also good of course.

Commit message: DOC: enhanced example with c.dtype [skip azp][skip cirrus][skip actions]

Patch to review:
diff --git a/numpy/lib/_function_base_impl.py b/numpy/lib/_function_base_impl.py
index ce661155dc4f..9a1ee7a64aed 100644
--- a/numpy/lib/_function_base_impl.py
+++ b/numpy/lib/_function_base_impl.py
@@ -5732,6 +5732,13 @@ def append(arr, values, axis=None):
     insert : Insert elements into an array.
     delete : Delete elements from an array.
 
+    Notes
+    -----
+    If dtype of `values` is different from the dtype of `arr` then
+    their dtypes are compared to figure out the common dtype they can
+    both be safely coerced to. For e.g. `int64` and `float64` can both
+    go to `float64`. See examples section below.
+
     Examples
     --------
     >>> np.append([1, 2, 3], [[4, 5, 6], [7, 8, 9]])
@@ -5743,6 +5750,7 @@ def append(arr, values, axis=None):
     array([[1, 2, 3],
            [4, 5, 6],
            [7, 8, 9]])
+
     >>> np.append([[1, 2, 3], [4, 5, 6]], [7, 8, 9], axis=0)
     Traceback (most recent call last):
         ...
@@ -5750,6 +5758,17 @@ def append(arr, values, axis=None):
     the array at index 0 has 2 dimension(s) and the array at index 1 has 1
     dimension(s)
 
+    >>> a = np.array([1, 2], dtype=int)
+    >>> b = []
+    >>> c = np.append(a, b)
+    >>> c
+    array([1., 2.])
+    >>> c.dtype
+    float64
+
+    Default dtype for empty lists is `float64` thus making the output of dtype
+    `float64` when appended with dtype `int64`
+
     """
     arr = asanyarray(arr)
     if axis is None:


Code review comments:
diff_hunk: @@ -5732,6 +5732,13 @@ def append(arr, values, axis=None):
     insert : Insert elements into an array.
     delete : Delete elements from an array.
 
+    Notes
+    -----
+    If dtype of `values` is different from the dtype of `arr` then
+    their dtypes are compared to figure out the common dtype they can
+    both be safely coerced to. For e.g. `int64` and `float64` can both
+    go to `float64`. See examples section below.
+
path: numpy/lib/_function_base_impl.py
text: @user1:
I think either the example or the note is sufficient, no need for both, and I prefer the example.