diff options
Diffstat (limited to 'doc/source/reference')
-rw-r--r-- | doc/source/reference/arrays.nditer.rst | 87 | ||||
-rw-r--r-- | doc/source/reference/c-api.iterator.rst | 28 |
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``. |