8000 Merge pull request #10949 from mattip/doc-nditer · numpy/numpy@0a02ea2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0a02ea2

Browse files
authored
Merge pull request #10949 from mattip/doc-nditer
DOC: cleanup documentation, continuation of nditer PR #9998
2 parents 79cd01d + 246ad1d commit 0a02ea2

File tree

7 files changed

+68
-36
lines changed

7 files changed

+68
-36
lines changed

doc/release/1.15.0-notes.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Deprecations
6464
anytime one of the iterator operands is writeable, so that numpy can
6565
manage writeback semantics, or should call ``it.close()``. A
6666
`RuntimeWarning` will be emitted otherwise in these cases. Users of the C-API
67-
should call ``NpyIter_Close`` before ``NpyIter_Dealloc``.
67+
should call ``NpyIter_Close`` before ``NpyIter_Deallocate``.
6868

6969

7070
Future Changes
@@ -120,8 +120,8 @@ using the old API.
120120
C API changes
121121
=============
122122

123-
``NpyIter_Close`` has been added and should be called before
124-
``NpyIter_Dealloc`` to resolve possible writeback-enabled arrays.
123+
* ``NpyIter_Close`` has been added and should be called before
124+
``NpyIter_Deallocate`` to resolve possible writeback-enabled arrays.
125125

126126
New Features
127127
============

doc/source/reference/arrays.nditer.rst

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -78,27 +78,28 @@ order='C' for C order and order='F' for Fortran order.
7878
...
7979
0 3 1 4 2 5
8080

81+
.. _nditer-context-manager:
82+
8183
Modifying Array Values
8284
----------------------
8385

84-
By default, the :class:`nditer` treats the input array as a read-only
85-
object. To modify the array elements, you must specify either read-write
86-
or write-only mode. This is controlled with per-operand flags. The
87-
operands may be created as views into the original data with the
88-
`WRITEBACKIFCOPY` flag. In this case the iterator must either
89-
90-
- be used as a context manager, and the temporary data will be written back
91-
to the original array when the `__exit__` function is called.
92-
- have a call to the iterator's `close` function to ensure the modified data
93-
is written back to the original array.
94-
95-
Regular assignment in Python simply changes a reference in the local or
96-
global variable dictionary instead of modifying an existing variable in
97-
place. This means that simply assigning to `x` will not place the value
98-
into the element of the array, but rather switch `x` from being an array
99-
element reference to being a reference to the value you assigned. To
100-
actually modify the element of the array, `x` should be indexed with
101-
the ellipsis.
86+
By default, the :class:`nditer` treats the input operand as a read-only
87+
object. To be able to modify the array elements, you must specify either
88+
read-write or write-only mode using the `'readwrite'` or `'writeonly'`
89+
per-operand flags.
90+
91+
The nditer will then yield writeable buffer arrays which you may modify. However,
92+
because the nditer must copy this buffer data back to the original array once
93+
iteration is finished, you must signal when the iteration is ended, by one of two
94+
methods. You may either:
95+
96+
- used the nditer as a context manager using the `with` statement, and
97+
the temporary data will be written back when the context is exited.
98+
- call the iterator's `close` method once finished iterating, which will trigger
99+
the write-back.
100+
101+
The nditer can no longer be iterated once either `close` is called or its
102+
context is exited.
102103

103104
.. admonition:: Example
104105

@@ -186,7 +187,7 @@ construct in order to be more readable.
186187
0 <(0, 0)> 1 <(0, 1)> 2 <(0, 2)> 3 <(1, 0)> 4 <(1, 1)> 5 <(1, 2)>
187188

188189
>>> it = np.nditer(a, flags=['multi_index'], op_flags=['writeonly'])
189-
>>> with it:
190+
>>> with it:
190191
.... while not it.finished:
191192
... it[0] = it.multi_index[1] - it.multi_index[0]
192193
... it.iternext()

doc/source/reference/c-api.iterator.rst

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,10 @@ Construction and Destruction
709709
the functions will pass back errors through it instead of setting
710710
a Python exception.
711711
712+
:c:func:`NpyIter_Deallocate` must be called for each copy. One call to
713+
:c:func:`NpyIter_Close` is sufficient to trigger writeback resolution for
714+
all copies since they share buffers.
715+
712716
.. c:function:: int NpyIter_RemoveAxis(NpyIter* iter, int axis)``
713717
714718
Removes an axis from iteration. This requires that
@@ -761,17 +765,19 @@ Construction and Destruction
761765
762766
.. c:function:: int NpyIter_Close(NpyIter* iter)
763767
764-
Resolves any needed writeback resolution. Must be called before
765-
``NpyIter_Deallocate``. After this call it is not safe to use the operands.
768+
Resolves any needed writeback resolution. Should be called before
769+
:c:func::`NpyIter_Deallocate`. After this call it is not safe to use the operands.
770+
When using :c:func:`NpyIter_Copy`, only one call to :c:func:`NpyIter_Close`
771+
is sufficient to resolve any writebacks, since the copies share buffers.
766772
767773
Returns ``0`` or ``-1`` if unsuccessful.
768774
769775
.. c:function:: int NpyIter_Deallocate(NpyIter* iter)
770776
771777
Deallocates the iterator object.
772778
773-
`NpyIter_Close` should be called before this. If not, and if writeback is
774-
needed, it will be performed at this point in order to maintain
779+
:c:func:`NpyIter_Close` should be called before this. If not, and if
780+
writeback is needed, it will be performed at this point in order to maintain
775781
backward-compatibility with older code, and a deprecation warning will be
776782
emitted. Old code should be updated to call `NpyIter_Close` beforehand.
777783

numpy/add_newdocs.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -385,10 +385,11 @@ def luf(lamdaexpr, *args, **kwargs):
385385
array([ 0.5, 1.5, 4.5, 9.5, 16.5])
386386
387387
If operand flags `"writeonly"` or `"readwrite"` are used the operands may
388-
be views into the original data with the WRITEBACKIFCOPY flag. In this case
389-
nditer must be used as a context manager. The temporary
390-
data will be written back to the original data when the `` __exit__``
391-
function is called but not before::
388+
be views into the original data with the `WRITEBACKIFCOPY` flag. In this case
389+
nditer must be used as a context manager or the nditer.close
390+
method must be called before using the result. The temporary
391+
data will be written back to the original data when the `__exit__`
392+
function is called but not before:
392393
393394
>>> a = np.arange(6, dtype='i4')[::-2]
394395
>>> with nditer(a, [],
@@ -405,7 +406,7 @@ def luf(lamdaexpr, *args, **kwargs):
405406
references (like `x` in the example) may or may not share data with
406407
the original data `a`. If writeback semantics were active, i.e. if
407408
`x.base.flags.writebackifcopy` is `True`, then exiting the iterator
408-
will sever the connection between `x` and `a`, writing to `x` will
409+
will sever the connection between `x` and `a`, writing to `x` will
409410
no longer write to `a`. If writeback semantics are not active, then
410411
`x.data` will still point at some part of `a.data`, and writing to
411412
one will affect the other.
@@ -566,6 +567,11 @@ def luf(lamdaexpr, *args, **kwargs):
566567
567568
Resolve all writeback semantics in writeable operands.
568569
570+
See Also
571+
--------
572+
573+
:ref:`nditer-context-manager`
574+
569575
"""))
570576

571577

numpy/core/code_generators/numpy_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
Whenever you change one index, you break the ABI (and the ABI version number
77
should be incremented). Whenever you add an item to one of the dict, the API
88
needs to be updated in both setup_common.py and by adding an appropriate
9-
entry to cversion.txt (generate the hash via "python cversions.py".
9+
entry to cversion.txt (generate the hash via "python cversions.py").
1010
1111
When adding a function, make sure to use the next integer not used as an index
1212
(in case you use an existing index or jump, the build will stop and raise an

numpy/core/src/multiarray/arrayobject.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ NPY_NO_EXPORT int
8686
PyArray_SetUpdateIfCopyBase(PyArrayObject *arr, PyArrayObject *base)
8787
{
8888
int ret;
89-
/* 2017-Nov-10 1.14 */
89+
/* 2017-Nov -10 1.14 (for PyPy only) */
90+
/* 2018-April-21 1.15 (all Python implementations) */
9091
if (DEPRECATE("PyArray_SetUpdateIfCopyBase is deprecated, use "
9192
"PyArray_SetWritebackIfCopyBase instead, and be sure to call "
9293
"PyArray_ResolveWritebackIfCopy before the array is deallocated, "

numpy/core/tests/test_nditer.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ def test_iter_nbo_align_contig():
811811
assert_equal(i.operands[0], a)
812812
i.operands[0][:] = 2
813813
assert_equal(au, [2]*6)
814-
i = None # should not raise a DeprecationWarning
814+
del i # should not raise a warning
815815
# Byte order change by requesting NBO
816816
a = np.arange(6, dtype='f4')
817817
au = a.byteswap().newbyteorder()
@@ -2838,12 +2838,30 @@ def test_writebacks():
28382838
it = nditer(au, [],
28392839
[['readwrite', 'updateifcopy']],
28402840
casting='equiv', op_dtypes=[np.dtype('f4')])
2841-
au = None
2841+
# reentering works
2842+
with it:
2843+
with it:
2844+
for x in it:
2845+
x[...] = 123
2846+
2847+
it = nditer(au, [],
2848+
[['readwrite', 'updateifcopy']],
2849+
casting='equiv', op_dtypes=[np.dtype('f4')])
2850+
# make sure exiting the inner context manager closes the iterator
2851+
with it:
2852+
with it:
2853+
for x in it:
2854+
x[...] = 123
2855+
assert_raises(ValueError, getattr, it, 'operands')
28422856
# do not crash if original data array is decrefed
2857+
it = nditer(au, [],
2858+
[['readwrite', 'updateifcopy']],
2859+
casting='equiv', op_dtypes=[np.dtype('f4')])
2860+
del au
28432861
with it:
28442862
for x in it:
28452863
x[...] = 123
2846-
# make sure we cannot reenter the iterand
2864+
# make sure we cannot reenter the closed iterator
28472865
enter = it.__enter__
28482866
assert_raises(ValueError, enter)
28492867

0 commit comments

Comments
 (0)
0