summaryrefslogtreecommitdiff
path: root/doc/source/reference
diff options
context:
space:
mode:
Diffstat (limited to 'doc/source/reference')
-rw-r--r--doc/source/reference/arrays.nditer.rst87
-rw-r--r--doc/source/reference/c-api.iterator.rst28
2 files changed, 73 insertions, 42 deletions
diff --git a/doc/source/reference/arrays.nditer.rst b/doc/source/reference/arrays.nditer.rst
index 76f5991cf..911ff6671 100644
--- a/doc/source/reference/arrays.nditer.rst
+++ b/doc/source/reference/arrays.nditer.rst
@@ -83,7 +83,14 @@ Modifying Array Values
By default, the :class:`nditer` treats the input array as a read-only
object. To modify the array elements, you must specify either read-write
-or write-only mode. This is controlled with per-operand flags.
+or write-only mode. This is controlled with per-operand flags. The
+operands may be created as views into the original data with the
+`WRITEBACKIFCOPY` flag. In this case the iterator must either
+
+- be used as a context manager, and the temporary data will be written back
+ to the original array when the `__exit__` function is called.
+- have a call to the iterator's `close` function to ensure the modified data
+ is written back to the original array.
Regular assignment in Python simply changes a reference in the local or
global variable dictionary instead of modifying an existing variable in
@@ -99,8 +106,9 @@ the ellipsis.
>>> a
array([[0, 1, 2],
[3, 4, 5]])
- >>> for x in np.nditer(a, op_flags=['readwrite']):
- ... x[...] = 2 * x
+ >>> with np.nditer(a, op_flags=['readwrite']) as it:
+ ... for x in it:
+ ... x[...] = 2 * x
...
>>> a
array([[ 0, 2, 4],
@@ -178,9 +186,10 @@ construct in order to be more readable.
0 <(0, 0)> 1 <(0, 1)> 2 <(0, 2)> 3 <(1, 0)> 4 <(1, 1)> 5 <(1, 2)>
>>> it = np.nditer(a, flags=['multi_index'], op_flags=['writeonly'])
- >>> while not it.finished:
- ... it[0] = it.multi_index[1] - it.multi_index[0]
- ... it.iternext()
+ >>> with it:
+ .... while not it.finished:
+ ... it[0] = it.multi_index[1] - it.multi_index[0]
+ ... it.iternext()
...
>>> a
array([[ 0, 1, 2],
@@ -426,9 +435,10 @@ reasons.
... flags = ['external_loop', 'buffered'],
... op_flags = [['readonly'],
... ['writeonly', 'allocate', 'no_broadcast']])
- ... for x, y in it:
- ... y[...] = x*x
- ... return it.operands[1]
+ ... with it:
+ ... for x, y in it:
+ ... y[...] = x*x
+ ... return it.operands[1]
...
>>> square([1,2,3])
@@ -505,9 +515,10 @@ For a simple example, consider taking the sum of all elements in an array.
>>> a = np.arange(24).reshape(2,3,4)
>>> b = np.array(0)
- >>> for x, y in np.nditer([a, b], flags=['reduce_ok', 'external_loop'],
- ... op_flags=[['readonly'], ['readwrite']]):
- ... y[...] += x
+ >>> with np.nditer([a, b], flags=['reduce_ok', 'external_loop'],
+ ... op_flags=[['readonly'], ['readwrite']]) as it:
+ ... for x,y in it:
+ ... y[...] += x
...
>>> b
array(276)
@@ -525,11 +536,12 @@ sums along the last axis of `a`.
>>> it = np.nditer([a, None], flags=['reduce_ok', 'external_loop'],
... op_flags=[['readonly'], ['readwrite', 'allocate']],
... op_axes=[None, [0,1,-1]])
- >>> it.operands[1][...] = 0
- >>> for x, y in it:
- ... y[...] += x
+ >>> with it:
+ ... it.operands[1][...] = 0
+ ... for x, y in it:
+ ... y[...] += x
...
- >>> it.operands[1]
+ ... it.operands[1]
array([[ 6, 22, 38],
[54, 70, 86]])
>>> np.sum(a, axis=2)
@@ -558,12 +570,13 @@ buffering.
... 'buffered', 'delay_bufalloc'],
... op_flags=[['readonly'], ['readwrite', 'allocate']],
... op_axes=[None, [0,1,-1]])
- >>> it.operands[1][...] = 0
- >>> it.reset()
- >>> for x, y in it:
- ... y[...] += x
+ >>> with it:
+ ... it.operands[1][...] = 0
+ ... it.reset()
+ ... for x, y in it:
+ ... y[...] += x
...
- >>> it.operands[1]
+ ... it.operands[1]
array([[ 6, 22, 38],
[54, 70, 86]])
@@ -609,11 +622,12 @@ Here's how this looks.
... op_flags=[['readonly'], ['readwrite', 'allocate']],
... op_axes=[None, axeslist],
... op_dtypes=['float64', 'float64'])
- ... it.operands[1][...] = 0
- ... it.reset()
- ... for x, y in it:
- ... y[...] += x*x
- ... return it.operands[1]
+ ... with it:
+ ... it.operands[1][...] = 0
+ ... it.reset()
+ ... for x, y in it:
+ ... y[...] += x*x
+ ... return it.operands[1]
...
>>> a = np.arange(6).reshape(2,3)
>>> sum_squares_py(a)
@@ -661,16 +675,17 @@ Here's the listing of sum_squares.pyx::
op_flags=[['readonly'], ['readwrite', 'allocate']],
op_axes=[None, axeslist],
op_dtypes=['float64', 'float64'])
- it.operands[1][...] = 0
- it.reset()
- for xarr, yarr in it:
- x = xarr
- y = yarr
- size = x.shape[0]
- for i in range(size):
- value = x[i]
- y[i] = y[i] + value * value
- return it.operands[1]
+ with it:
+ it.operands[1][...] = 0
+ it.reset()
+ for xarr, yarr in it:
+ x = xarr
+ y = yarr
+ size = x.shape[0]
+ for i in range(size):
+ value = x[i]
+ y[i] = y[i] + value * value
+ return it.operands[1]
On this machine, building the .pyx file into a module looked like the
following, but you may have to find some Cython tutorials to tell you
diff --git a/doc/source/reference/c-api.iterator.rst b/doc/source/reference/c-api.iterator.rst
index 4c59bce51..314b62a16 100644
--- a/doc/source/reference/c-api.iterator.rst
+++ b/doc/source/reference/c-api.iterator.rst
@@ -110,6 +110,7 @@ number of non-zero elements in an array.
/* Increment the iterator to the next inner loop */
} while(iternext(iter));
+ NpyIter_Close(iter) /* best practice, not strictly required in this case */
NpyIter_Deallocate(iter);
return nonzero_count;
@@ -194,6 +195,7 @@ is used to control the memory layout of the allocated result, typically
ret = NpyIter_GetOperandArray(iter)[1];
Py_INCREF(ret);
+ NpyIter_Close(iter);
if (NpyIter_Deallocate(iter) != NPY_SUCCEED) {
Py_DECREF(ret);
return NULL;
@@ -490,7 +492,10 @@ Construction and Destruction
Indicate how the user of the iterator will read or write
to ``op[i]``. Exactly one of these flags must be specified
- per operand.
+ per operand. Using ``NPY_ITER_READWRITE`` or ``NPY_ITER_WRITEONLY``
+ for a user-provided operand may trigger `WRITEBACKIFCOPY``
+ semantics. The data will be written back to the original array
+ when ``NpyIter_Close`` is called.
.. c:var:: NPY_ITER_COPY
@@ -502,12 +507,12 @@ Construction and Destruction
Triggers :c:data:`NPY_ITER_COPY`, and when an array operand
is flagged for writing and is copied, causes the data
- in a copy to be copied back to ``op[i]`` when the iterator
- is destroyed.
+ in a copy to be copied back to ``op[i]`` when ``NpyIter_Close`` is
+ called.
If the operand is flagged as write-only and a copy is needed,
an uninitialized temporary array will be created and then copied
- to back to ``op[i]`` on destruction, instead of doing
+ to back to ``op[i]`` on calling ``NpyIter_Close``, instead of doing
the unnecessary copy operation.
.. c:var:: NPY_ITER_NBO
@@ -754,10 +759,21 @@ Construction and Destruction
Returns ``NPY_SUCCEED`` or ``NPY_FAIL``.
+.. c:function:: int NpyIter_Close(NpyIter* iter)
+
+ Resolves any needed writeback resolution. Must be called before
+ ``NpyIter_Deallocate``. After this call it is not safe to use the operands.
+
+ Returns ``0`` or ``-1`` if unsuccessful.
+
.. c:function:: int NpyIter_Deallocate(NpyIter* iter)
- Deallocates the iterator object. This additionally frees any
- copies made, triggering UPDATEIFCOPY behavior where necessary.
+ Deallocates the iterator object.
+
+ `NpyIter_Close` should be called before this. If not, and if writeback is
+ needed, it will be performed at this point in order to maintain
+ backward-compatibility with older code, and a deprecation warning will be
+ emmitted. Old code shuold be updated to call `NpyIter_Close` beforehand.
Returns ``NPY_SUCCEED`` or ``NPY_FAIL``.