API итератора массивов
Новая версия с 1.6.
Итератор массивов
Итератор массивов инкапсулирует множество ключевых функций ufuncs, позволяя коду пользователя поддерживать такие функции, как параметры вывода, сохранение структуры памяти и буферизацию данных с неправильным выравниванием или типом, без необходимости сложного программирования.
Эта страница описывает API итератора. Итератор называется NpyIter, а функции называются NpyIter_*.
Существует вводное руководство по итерации массивов, которое может быть полезно для тех, кто использует этот C API. Во многих случаях для тестирования идей рекомендуется сначала создать итератор в Python, прежде чем писать код итерации на C.
Пример простой итерации
Лучший способ ознакомиться с итератором — изучить его использование в самом коде NumPy. Например, здесь представлена немного изменённая версия кода для PyArray_CountNonzero, которая подсчитывает количество ненулевых элементов в массиве.
npy_intp PyArray_CountNonzero(PyArrayObject* self)
{
/* Nonzero boolean function */
PyArray_NonzeroFunc* nonzero = PyArray_DESCR(self)->f->nonzero;
NpyIter* iter;
NpyIter_IterNextFunc *iternext;
char** dataptr;
npy_intp nonzero_count;
npy_intp* strideptr,* innersizeptr;
/* Handle zero-sized arrays specially */
if (PyArray_SIZE(self) == 0) {
return 0;
}
/*
* Create and use an iterator to count the nonzeros.
* flag NPY_ITER_READONLY
* - The array is never written to.
* flag NPY_ITER_EXTERNAL_LOOP
* - Inner loop is done outside the iterator for efficiency.
* flag NPY_ITER_NPY_ITER_REFS_OK
* - Reference types are acceptable.
* order NPY_KEEPORDER
* - Visit elements in memory order, regardless of strides.
* This is good for performance when the specific order
* elements are visited is unimportant.
* casting NPY_NO_CASTING
* - No casting is required for this operation.
*/
iter = NpyIter_New(self, NPY_ITER_READONLY|
NPY_ITER_EXTERNAL_LOOP|
NPY_ITER_REFS_OK,
NPY_KEEPORDER, NPY_NO_CASTING,
NULL);
if (iter == NULL) {
return -1;
}
/*
* The iternext function gets stored in a local variable
* so it can be called repeatedly in an efficient manner.
*/
iternext = NpyIter_GetIterNext(iter, NULL);
if (iternext == NULL) {
NpyIter_Deallocate(iter);
return -1;
}
/* The location of the data pointer which the iterator may update */
dataptr = NpyIter_GetDataPtrArray(iter);
/* The location of the stride which the iterator may update */
strideptr = NpyIter_GetInnerStrideArray(iter);
/* The location of the inner loop size which the iterator may update */
innersizeptr = NpyIter_GetInnerLoopSizePtr(iter);
nonzero_count = 0;
do {
/* Get the inner loop data/stride/count values */
char* data = *dataptr;
npy_intp stride = *strideptr;
npy_intp count = *innersizeptr;
/* This is a typical inner loop for NPY_ITER_EXTERNAL_LOOP */
while (count--) {
if (nonzero(data, self)) {
++nonzero_count;
}
data += stride;
}
/* Increment the iterator to the next inner loop */
} while(iternext(iter));
NpyIter_Deallocate(iter);
return nonzero_count;
}
Пример простой многомерной итерации
Вот пример функции копирования, использующей итератор. Параметр order используется для управления структурой памяти создаваемого результата, обычно желательно NPY_KEEPORDER.
PyObject *CopyArray(PyObject *arr, NPY_ORDER order)
{
NpyIter *iter;
NpyIter_IterNextFunc *iternext;
PyObject *op[2], *ret;
npy_uint32 flags;
npy_uint32 op_flags[2];
npy_intp itemsize, *innersizeptr, innerstride;
char **dataptrarray;
/*
* No inner iteration - inner loop is handled by CopyArray code
*/
flags = NPY_ITER_EXTERNAL_LOOP;
/*
* Tell the constructor to automatically allocate the output.
* The data type of the output will match that of the input.
*/
op[0] = arr;
op[1] = NULL;
op_flags[0] = NPY_ITER_READONLY;
op_flags[1] = NPY_ITER_WRITEONLY | NPY_ITER_ALLOCATE;
/* Construct the iterator */
iter = NpyIter_MultiNew(2, op, flags, order, NPY_NO_CASTING,
op_flags, NULL);
if (iter == NULL) {
return NULL;
}
/*
* Make a copy of the iternext function pointer and
* a few other variables the inner loop needs.
*/
iternext = NpyIter_GetIterNext(iter, NULL);
innerstride = NpyIter_GetInnerStrideArray(iter)[0];
itemsize = NpyIter_GetDescrArray(iter)[0]->elsize;
/*
* The inner loop size and data pointers may change during the
* loop, so just cache the addresses.
*/
innersizeptr = NpyIter_GetInnerLoopSizePtr(iter);
dataptrarray = NpyIter_GetDataPtrArray(iter);
/*
* Note that because the iterator allocated the output,
* it matches the iteration order and is packed tightly,
* so we don't need to check it like the input.
*/
if (innerstride == itemsize) {
do {
memcpy(dataptrarray[1], dataptrarray[0],
itemsize * (*innersizeptr));
} while (iternext(iter));
} else {
/* For efficiency, should specialize this based on item size... */
npy_intp i;
do {
npy_intp size = *innersizeptr;
char *src = dataptrarray[0], *dst = dataptrarray[1];
for(i = 0; i < size; i++, src += innerstride, dst += itemsize) {
memcpy(dst, src, itemsize);
}
} while (iternext(iter));
}
/* Get the result from the iterator object array */
ret = NpyIter_GetOperandArray(iter)[1];
Py_INCREF(ret);
if (NpyIter_Deallocate(iter) != NPY_SUCCEED) {
Py_DECREF(ret);
return NULL;
}
return ret;
}
Типы данных итератора
Структура итератора — это внутренняя деталь, и код пользователя видит только неполную структуру.
-
NpyIter -
Это тип указателя-объекта для итератора. Доступ к его содержимому можно получить только через API итератора.
-
NpyIter_Type -
Это тип, который экспонирует итератор для Python. В настоящее время нет API, предоставляющего доступ к значениям итератора, созданного в Python. Если итератор создан в Python, он должен использоваться в Python, и наоборот. Такой API, вероятно, будет создан в будущей версии.
-
NpyIter_IterNextFunc -
Это указатель на функцию для цикла итерации, возвращаемый
NpyIter_GetIterNext.
-
NpyIter_GetMultiIndexFunc -
Это указатель на функцию для получения текущего многомерного индекса итератора, возвращаемый
NpyIter_GetGetMultiIndex.
Создание и уничтожение
-
NpyIter* NpyIter_New(PyArrayObject* op, npy_uint32 flags, NPY_ORDER order, NPY_CASTING casting, PyArray_Descr* dtype) -
Создаёт итератор для заданного объекта массива NumPy
op.Флаги, которые можно передать в
flags— это любое сочетание глобальных и операторных флагов, описанных вNpyIter_MultiNew, за исключениемNPY_ITER_ALLOCATE.Любое значение перечисления
NPY_ORDERможно передать вorder. Для эффективной итерацииNPY_KEEPORDER— лучший вариант, а другие порядки навязывают определённый порядок итерации.Любое значение перечисления
NPY_CASTINGможно передать вcasting. Значения включаютNPY_NO_CASTING,NPY_EQUIV_CASTING,NPY_SAFE_CASTING,NPY_SAME_KIND_CASTINGиNPY_UNSAFE_CASTING. Для разрешения преобразований также необходимо включить копирование или буферизацию.Если
dtypeнеNULL, то он требует этого типа данных. Если копирование разрешено, то при возможности преобразования данных будет создана временная копия. ЕслиNPY_ITER_UPDATEIFCOPYвключён, при уничтожении итератора данные будут также скопированы обратно с другим преобразованием.Возвращает NULL, если произошла ошибка, в противном случае возвращает созданный итератор.
Для создания итератора, аналогичного старому, это должно работать.
iter = NpyIter_New(op, NPY_ITER_READWRITE, NPY_CORDER, NPY_NO_CASTING, NULL);Если необходимо изменить массив с выровненным
doubleкодом, но порядок не имеет значения, используйте это.dtype = PyArray_DescrFromType(NPY_DOUBLE); iter = NpyIter_New(op, NPY_ITER_READWRITE| NPY_ITER_BUFFERED| NPY_ITER_NBO| NPY_ITER_ALIGNED, NPY_KEEPORDER, NPY_SAME_KIND_CASTING, dtype); Py_DECREF(dtype);
-
NpyIter* NpyIter_MultiNew(npy_intp nop, PyArrayObject** op, npy_uint32 flags, NPY_ORDER order, NPY_CASTING casting, npy_uint32* op_flags, PyArray_Descr** op_dtypes) -
Создаёт итератор для трансляции массивов
nopобъектов, предоставленных вop, используя стандартные правила трансляции NumPy.Любое из значений перечисления
NPY_ORDERможет быть передано вorder. Для эффективной итерации лучшим вариантом являетсяNPY_KEEPORDER, а другие порядки навязывают определённый порядок итерации. При использованииNPY_KEEPORDER, чтобы также гарантировать, что итерация не обращается по оси, нужно передать флагNPY_ITER_DONT_NEGATE_STRIDES.Любое из значений перечисления
NPY_CASTINGможет быть передано вcasting. Значения включаютNPY_NO_CASTING,NPY_EQUIV_CASTING,NPY_SAFE_CASTING,NPY_SAME_KIND_CASTINGиNPY_UNSAFE_CASTING. Для разрешения преобразований также необходимо включить копирование или буферизацию.Если
op_dtypesнеNULL, это указывает на тип данных илиNULLдля каждогоop[i].Возвращает NULL, если произошла ошибка, в противном случае возвращает выделенный итератор.
Флаги, которые могут быть переданы в
flags, применяемые ко всему итератору, это:-
NPY_ITER_C_INDEX -
Заставляет итератор отслеживать развёрнутый плоский индекс, соответствующий порядку C. Этот параметр нельзя использовать с
NPY_ITER_F_INDEX.
-
NPY_ITER_F_INDEX -
Заставляет итератор отслеживать развёрнутый плоский индекс, соответствующий порядку Fortran. Этот параметр нельзя использовать с
NPY_ITER_C_INDEX.
-
NPY_ITER_MULTI_INDEX -
Заставляет итератор отслеживать многомерный индекс. Это предотвращает слияние осей для создания больших внутренних циклов. Если цикл также не буферизован и не отслеживается индекс (
NpyIter_RemoveAxisможет быть вызван), то размер итератора может быть-1для указания того, что итератор слишком велик. Это может произойти из-за сложной трансляции и приведёт к ошибкам при установке диапазона итератора, удалении многомерного индекса или получении следующей функции. Однако повторное удаление осей и использование итератора в обычном режиме возможно, если размер достаточно мал после удаления.
-
NPY_ITER_EXTERNAL_LOOP -
Заставляет итератор пропустить итерацию внутреннего цикла, требуя от пользователя обработки.
Этот флаг несовместим с
NPY_ITER_C_INDEX,NPY_ITER_F_INDEXиNPY_ITER_MULTI_INDEX.
-
NPY_ITER_DONT_NEGATE_STRIDES -
Это влияет на итератор только при указании
NPY_KEEPORDERдля параметра порядка. По умолчанию, при использованииNPY_KEEPORDER, итератор переворачивает оси с отрицательными шагами, чтобы память проходилась в прямом направлении. Этот шаг отключается. Используйте этот флаг, если вы хотите использовать внутреннее упорядочение памяти осей, но не хотите, чтобы ось была перевёрнута. Такое поведение, например, уnumpy.ravel(a, order='K').
-
NPY_ITER_COMMON_DTYPE -
Заставляет итератор преобразовать все операнды в общий тип данных, рассчитанный на основе правил повышения типа ufunc. Необходимо включить копирование или буферизацию.
Если общий тип данных известен заранее, не используйте этот флаг. Вместо этого установите требуемый тип данных для всех операндов.
-
NPY_ITER_REFS_OK -
Указывает, что массивы ссылочного типа (массивы объектов или структурированные массивы, содержащие объект) могут быть приняты и использованы в итераторе. Если этот флаг включён, вызывающая сторона должна убедиться, что
NpyIter_IterationNeedsAPI(iter)истинно, в этом случае она не может освободить GIL во время итерации.
-
NPY_ITER_ZEROSIZE_OK -
Указывает, что массивы с размером 0 должны быть разрешены. Поскольку типичный цикл итерации не работает с массивами нулевого размера, нужно проверить, что IterSize больше нуля, прежде чем входить в цикл итерации. В настоящее время проверяются только операнды, а не принудительная форма.
-
NPY_ITER_REDUCE_OK -
Разрешает изменяемые операнды с размерностью с шагом 0 и размером больше 1. Обратите внимание, что такие операнды должны быть читаемыми/записываемыми.
При включённой буферизации также переключается на специальный режим буферизации, который уменьшает длину цикла по мере необходимости, чтобы не перезаписывать значения, которые уменьшаются.
Обратите внимание, что если вы хотите выполнить уменьшение на автоматически выделенном выходе, вам необходимо использовать
NpyIter_GetOperandArrayдля получения ссылки, а затем установить каждое значение на единицу уменьшения перед выполнением цикла итерации. В случае буферизованного уменьшения это также означает, что вы должны указать флагNPY_ITER_DELAY_BUFALLOC, а затем сбросить итератор после инициализации выделенного операнда для подготовки буферов.
-
NPY_ITER_RANGED -
Включает поддержку итерации по поддиапазонам полного диапазона
iterindexдиапазона[0, NpyIter_IterSize(iter)). Используйте функциюNpyIter_ResetToIterIndexRangeдля указания диапазона итерации.Этот флаг может использоваться только с
NPY_ITER_EXTERNAL_LOOPпри включённомNPY_ITER_BUFFERED. Это связано с тем, что без буферизации внутренний цикл всегда имеет размер наименьшей итерационной размерности, а разрешение его разбиения потребовало бы специальной обработки, фактически делая его более похожим на буферизованную версию.
-
NPY_ITER_BUFFERED -
Заставляет итератор хранить данные буферизации и использовать буферизацию для удовлетворения требований к типу данных, выравниванию и порядку байтов. Чтобы буферизовать операнд, не указывайте флаги
NPY_ITER_COPYилиNPY_ITER_UPDATEIFCOPY, так как они переопределят буферизацию. Буферизация особенно полезна для кода Python, использующего итератор, позволяя работать с большими блоками данных сразу, чтобы амортизировать накладные расходы интерпретатора Python.Если используется с
NPY_ITER_EXTERNAL_LOOP, внутренний цикл для вызывающей стороны может получать более крупные блоки, чем это возможно без буферизации, из-за того, как организованы шаги.Обратите внимание, что если операнду задан флаг
NPY_ITER_COPYилиNPY_ITER_UPDATEIFCOPY, будет сделана копия вместо буферизации. Буферизация всё ещё будет происходить, когда массив был транслирован, поэтому элементы должны быть дублированы, чтобы получить постоянный шаг.В нормальной буферизации размер каждого внутреннего цикла равен размеру буфера или, возможно, больше, если указан
NPY_ITER_GROWINNER. Если включёнNPY_ITER_REDUCE_OKи происходит уменьшение, внутренние циклы могут стать меньше в зависимости от структуры уменьшения.
-
NPY_ITER_GROWINNER -
При включённой буферизации это позволяет размеру внутреннего цикла увеличиваться, когда буферизация не нужна. Этот параметр лучше всего использовать, если вы выполняете прямой проход через все данные, а не что-либо со связанными малыми кэшируемыми массивами временных значений для каждого внутреннего цикла.
-
NPY_ITER_DELAY_BUFALLOC -
При включённой буферизации это откладывает выделение буферов до вызова
NpyIter_Resetили другой функции сброса. Этот флаг существует для предотвращения ненужного копирования данных буфера при создании нескольких копий буферизованного итератора для многопоточной итерации.Другое использование этого флага — для настройки операций уменьшения. После создания итератора и автоматического выделения результата уменьшения итератором (убедитесь, что используется доступ READWRITE), его значение может быть инициализировано до единицы уменьшения. Используйте
NpyIter_GetOperandArrayдля получения объекта. Затем вызовитеNpyIter_Resetдля выделения и заполнения буферов их начальными значениями.
Флаги, которые могут быть переданы в
op_flags[i], где0 <= i < nop:-
NPY_ITER_READWRITE
-
NPY_ITER_READONLY
-
NPY_ITER_WRITEONLY -
Указывают, как пользователь итератора будет читать или записывать в
op[i]. Должен быть указан ровно один из этих флагов на операнд.
-
NPY_ITER_COPY -
Разрешает создание копии
op[i], если она не соответствует требованиям к типу данных или выравниванию, как указано в флагах и параметрах конструктора.
-
-
NPY_ITER_UPDATEIFCOPY -
Вызывает
NPY_ITER_COPY, и когда операнд массива помечен для записи и копируется, вызывает копирование данных в копию обратно вop[i]при уничтожении итератора.Если операнд помечен как только для записи, и требуется копия, будет создан временный массив, который не инициализирован, а затем скопирован обратно в
op[i]при уничтожении, вместо ненужной операции копирования.
-
NPY_ITER_NBO
-
NPY_ITER_ALIGNED
-
NPY_ITER_CONTIG -
Принуждает итератор предоставлять данные для
op[i]в родном порядке байтов, выровненные согласно требованиям типа данных, непрерывные или любая комбинация.По умолчанию итератор производит указатели в предоставленные массивы, которые могут быть выровнены или не выровнены, а также с любым порядком байтов. Если копирование или буферизация не включены, и данные операнда не удовлетворяют ограничениям, будет поднято исключение.
Ограничение непрерывности применяется только к внутреннему циклу, последующие внутренние циклы могут иметь произвольные изменения указателей.
Если требуемый тип данных имеет неродной порядок байтов, флаг NBO переопределяет его, и требуемый тип данных преобразуется в родной порядок байтов.
-
NPY_ITER_ALLOCATE -
Это для выходных массивов и требует, чтобы флаг
NPY_ITER_WRITEONLYилиNPY_ITER_READWRITEбыл установлен. Еслиop[i]равен NULL, создает новый массив с окончательными размерами трансляции и структурой, соответствующей порядку итерации итератора.Когда
op[i]равен NULL, требуемый тип данныхop_dtypes[i]может быть также NULL, в этом случае он автоматически генерируется из типов данных массивов, помеченных как читаемые. Правила для генерации типа данных такие же, как для UFunc. Важно отметить обработку порядка байтов в выбранном типе данных. Если есть ровно один вход, тип данных входного массива используется как есть. В противном случае, если несколько типов данных входных массивов объединены вместе, выходной тип будет в родном порядке байтов.После выделения с этим флагом вызывающий может получить новый массив, вызвав
NpyIter_GetOperandArrayи получив i-й объект в возвращаемом C массиве. Вызывающий должен вызвать Py_INCREF на нем, чтобы потребовать ссылку на массив.
-
NPY_ITER_NO_SUBTYPE -
Для использования с
NPY_ITER_ALLOCATE, этот флаг отключает выделение подтипа массива для выхода, принуждая его быть прямым ndarray.TODO: Возможно, лучше было бы ввести функцию
NpyIter_GetWrappedOutputи убрать этот флаг?
-
NPY_ITER_NO_BROADCAST -
Обеспечивает, что входной или выходной массив точно соответствует размерам итерации.
-
NPY_ITER_ARRAYMASK -
Новое в версии 1.7.
Указывает, что этот операнд является маской для выбора элементов при записи в операнды, к которым применен флаг
NPY_ITER_WRITEMASKED. Только один операнд может иметь флагNPY_ITER_ARRAYMASK.Тип данных операнда с этим флагом должен быть либо
NPY_BOOL,NPY_MASK, или структурированный тип данных, поля которого являются допустимыми типами данных маски. В последнем случае он должен соответствовать структурированному операнду, имеющему флаг WRITEMASKED, так как он определяет маску для каждого поля этого массива.Этот флаг влияет только на запись из буфера обратно в массив. Это означает, что если операнд также имеет флаг
NPY_ITER_READWRITEилиNPY_ITER_WRITEONLY, код, выполняющий итерацию, может записывать в этот операнд, чтобы управлять тем, какие элементы останутся нетронутыми, а какие будут изменены. Это полезно, когда маска должна быть комбинацией входных масок, например. Значения масок могут быть созданы с помощью функцииNpyMask_Create.
-
NPY_ITER_WRITEMASKED -
Новое в версии 1.7.
Указывает, что только те элементы, которые операнд с флагом ARRAYMASK указывает, предназначены для изменения в ходе итерации. Вообще говоря, итератор этого не проверяет, это зависит от кода, выполняющего итерацию.
Код может использовать встроенную функцию
NpyMask_IsExposedдля проверки, разрешает ли маска на конкретном элементе запись.Когда используется этот флаг и этот операнд буферизован, это изменяет то, как данные копируются из буфера в массив. Используется процедура копирования с маской, которая копирует только элементы из буфера, для которых
NpyMask_IsExposedвозвращает true для соответствующего элемента в операнде ARRAYMASK.
-
-
NpyIter* NpyIter_AdvancedNew(npy_intp nop, PyArrayObject** op, npy_uint32 flags, NPY_ORDER order, NPY_CASTING casting, npy_uint32* op_flags, PyArray_Descr** op_dtypes, int oa_ndim, int** op_axes, npy_intp* itershape, npy_intp buffersize) -
Расширяет
NpyIter_MultiNewнесколькими расширенными параметрами, предоставляя больший контроль над трансляцией и буферизацией.Если значения -1/NULL передаются
oa_ndim,op_axes,itershape, иbuffersize, это эквивалентноNpyIter_MultiNew.Параметр
oa_ndim, когда не равен нулю или -1, задаёт количество измерений, которые будут итерированы с настроенной трансляцией. Если он предоставлен,op_axesдолжен иitershapeможет быть предоставлен. Параметрop_axesпозволяет детально управлять, как оси массивов операндов связываются и итерируются. Вop_axes, вы должны предоставить массив изnopуказателей на массивы размеромoa_ndimтипаnpy_intp. Если запись вop_axesравна NULL, применяются стандартные правила трансляции. Вop_axes[j][i]хранится либо допустимая осьop[j], либо -1, что означаетnewaxis. В каждом массивеop_axes[j]оси не могут повторяться. Следующий пример показывает, как стандартная трансляция применяется к 3-мерному массиву, 2-мерному массиву, 1-мерному массиву и скаляру.Примечание: До NumPy 1.8
oa_ndim == 0` was used for signalling that that ``op_axesиitershapeне используются. Это устаревшее свойство и должно быть заменено на -1. Для лучшей обратной совместимости можно использоватьNpyIter_MultiNewв этом случае.int oa_ndim = 3; /* # iteration axes */ int op0_axes[] = {0, 1, 2}; /* 3-D operand */ int op1_axes[] = {-1, 0, 1}; /* 2-D operand */ int op2_axes[] = {-1, -1, 0}; /* 1-D operand */ int op3_axes[] = {-1, -1, -1} /* 0-D (scalar) operand */ int* op_axes[] = {op0_axes, op1_axes, op2_axes, op3_axes};Параметр
itershapeпозволяет принудительно задать конкретную форму итерации для итератора. Это массив длиныoa_ndim. Когда запись отрицательна, её значение определяется из операндов. Этот параметр позволяет автоматически выделенным выходным массивам получить дополнительные измерения, не совпадающие с измерениями входных массивов.Если
buffersizeравно нулю, используется размер буфера по умолчанию, иначе он задаёт размер буфера.Возвращает NULL, если произошла ошибка, в противном случае возвращает выделенный итератор.
-
NpyIter* NpyIter_Copy(NpyIter* iter) -
Создаёт копию данного итератора. Эта функция предоставлена в основном для многопоточной итерации данных.
TODO: Переместить в раздел о многопоточной итерации.
Рекомендуемый подход к многопоточной итерации заключается в том, чтобы сначала создать итератор с флагами
NPY_ITER_EXTERNAL_LOOP,NPY_ITER_RANGED,NPY_ITER_BUFFERED,NPY_ITER_DELAY_BUFALLOCи, возможно,NPY_ITER_GROWINNER. Создать копию этого итератора для каждого потока (кроме одного для первого итератора). Затем взять диапазон индексов итерации[0, NpyIter_GetIterSize(iter))и разбить его на задачи, например, используя цикл TBB parallel_for. Когда поток получает задачу для выполнения, он использует свою копию итератора, вызываяNpyIter_ResetToIterIndexRangeи выполняя итерацию по всему диапазону.При использовании итератора в многопоточной программе или в коде, не держащем Python GIL, необходимо соблюдать осторожность, чтобы вызывать только функции, которые безопасны в данном контексте.
NpyIter_Copyне может быть безопасно вызван без Python GIL, потому что он увеличивает количество Python ссылок. ФункцииReset*и некоторые другие могут быть безопасно вызваны путём передачи вerrmsgпараметра не равного NULL, таким образом, что функции будут передавать ошибки через него, вместо того, чтобы устанавливать Python исключение.
-
int NpyIter_RemoveAxis(NpyIter* iter, int axis)`` -
Удаляет ось из итерации. Это требует, чтобы флаг
NPY_ITER_MULTI_INDEXбыл установлен для создания итератора, и не работает, если включена буферизация или отслеживается индекс. Эта функция также сбрасывает итератор в исходное состояние.Это полезно для создания цикла накопления, например. Итератор может быть сначала создан со всеми измерениями, включая ось накопления, таким образом, чтобы выходной массив создавался правильно. Затем ось накопления может быть удалена, а вычисление выполнено вложенным способом.
ПРЕДУПРЕЖДЕНИЕ: Эта функция может изменить внутреннюю структуру памяти итератора. Любые кэшированные функции или указатели из итератора должны быть получены заново! Диапазон итератора также будет сброшен.
Возвращает
NPY_SUCCEEDилиNPY_FAIL.
-
int NpyIter_RemoveMultiIndex(NpyIter* iter) -
Если итератор отслеживает многоиндекс, это удаляет поддержку для них и выполняет дальнейшую оптимизацию итератора, которая возможна, если многоиндексы не нужны. Эта функция также сбрасывает итератор в исходное состояние.
ПРЕДУПРЕЖДЕНИЕ: Эта функция может изменить внутреннюю структуру памяти итератора. Любые кэшированные функции или указатели из итератора необходимо получить снова!
После вызова этой функции,
NpyIter_HasMultiIndex(iter)вернёт false.Возвращает
NPY_SUCCEEDилиNPY_FAIL.
-
int NpyIter_EnableExternalLoop(NpyIter* iter) -
Если
NpyIter_RemoveMultiIndexбыл вызван, вы можете включить флагNPY_ITER_EXTERNAL_LOOP. Этот флаг запрещён вместе сNPY_ITER_MULTI_INDEX, поэтому эта функция предназначена для включения этой функции после вызоваNpyIter_RemoveMultiIndex. Эта функция также сбрасывает итератор в исходное состояние.ПРЕДУПРЕЖДЕНИЕ: Эта функция изменяет внутреннюю логику итератора. Любые кэшированные функции или указатели из итератора необходимо получить снова!
Возвращает
NPY_SUCCEEDилиNPY_FAIL.
-
int NpyIter_Deallocate(NpyIter* iter) -
Освобождает объект итератора. Это также освобождает любые сделанные копии, вызывая поведение UPDATEIFCOPY, когда это необходимо.
Возвращает
NPY_SUCCEEDилиNPY_FAIL.
-
int NpyIter_Reset(NpyIter* iter, char** errmsg) -
Сбрасывает итератор в исходное состояние, в начало диапазона итерации.
Возвращает
NPY_SUCCEEDилиNPY_FAIL. Если errmsg не NULL, исключение Python не устанавливается, когда возвращаетсяNPY_FAIL. Вместо этого *errmsg устанавливается в сообщение об ошибке. Когда errmsg не NULL, функция может быть безопасно вызвана без блокировки Python GIL.
-
int NpyIter_ResetToIterIndexRange(NpyIter* iter, npy_intp istart, npy_intp iend, char** errmsg) -
Сбрасывает итератор и ограничивает его диапазоном
iterindex[istart, iend). См.NpyIter_Copyдля объяснения использования этого для многопоточной итерации. Требуется, чтобы флагNPY_ITER_RANGEDбыл передан в конструктор итератора.Если вы хотите сбросить как диапазон
iterindex, так и базовые указатели одновременно, вы можете сделать следующее, чтобы избежать дополнительной копирования буфера (не забудьте добавить проверки кода возврата, когда копируете этот код)./* Set to a trivial empty range */ NpyIter_ResetToIterIndexRange(iter, 0, 0); /* Set the base pointers */ NpyIter_ResetBasePointers(iter, baseptrs); /* Set to the desired range */ NpyIter_ResetToIterIndexRange(iter, istart, iend);
Возвращает
NPY_SUCCEEDилиNPY_FAIL. Если errmsg не NULL, исключение Python не устанавливается, когда возвращаетсяNPY_FAIL. Вместо этого *errmsg устанавливается в сообщение об ошибке. Когда errmsg не NULL, функция может быть безопасно вызвана без блокировки Python GIL.
-
int NpyIter_ResetBasePointers(NpyIter *iter, char** baseptrs, char** errmsg) -
Сбрасывает итератор в исходное состояние, но использует значения в
baseptrsдля данных вместо указателей из массивов, по которым происходит итерация. Эта функция предназначена для использования вместе с параметромop_axesкодом вложенной итерации с двумя или более итераторами.Возвращает
NPY_SUCCEEDилиNPY_FAIL. Если errmsg не NULL, исключение Python не устанавливается, когда возвращаетсяNPY_FAIL. Вместо этого *errmsg устанавливается в сообщение об ошибке. Когда errmsg не NULL, функция может быть безопасно вызвана без блокировки Python GIL.TODO: Переместить следующее в специальный раздел о вложенных итераторах.
Создание итераторов для вложенной итерации требует некоторой осторожности. Все операнды итератора должны точно совпадать, иначе вызовы
NpyIter_ResetBasePointersбудут недействительны. Это означает, что автоматические копии и выделение вывода не должны использоваться произвольно. Вы по-прежнему можете использовать функции автоматического преобразования и приведения типов итератора, создав один из итераторов со всеми параметрами преобразования, включёнными, а затем взяв выделенные операнды с помощью функцииNpyIter_GetOperandArrayи передав их в конструкторы для остальных итераторов.ПРЕДУПРЕЖДЕНИЕ: При создании итераторов для вложенной итерации код не должен использовать размерность более одного раза в разных итераторах. Если это сделать, вложенная итерация создаст указатели за пределами границ во время итерации.
ПРЕДУПРЕЖДЕНИЕ: При создании итераторов для вложенной итерации буферизация может быть применена только к самому внутреннему итератору. Если буферизованный итератор используется в качестве источника для
baseptrs, он будет указывать на небольшой буфер, а не на массив, и внутренняя итерация будет недействительна.Шаблон использования вложенных итераторов выглядит следующим образом.
NpyIter *iter1, *iter1; NpyIter_IterNextFunc *iternext1, *iternext2; char **dataptrs1; /* * With the exact same operands, no copies allowed, and * no axis in op_axes used both in iter1 and iter2. * Buffering may be enabled for iter2, but not for iter1. */ iter1 = ...; iter2 = ...; iternext1 = NpyIter_GetIterNext(iter1); iternext2 = NpyIter_GetIterNext(iter2); dataptrs1 = NpyIter_GetDataPtrArray(iter1); do { NpyIter_ResetBasePointers(iter2, dataptrs1); do { /* Use the iter2 values */ } while (iternext2(iter2)); } while (iternext1(iter1));
-
int NpyIter_GotoMultiIndex(NpyIter* iter, npy_intp* multi_index) -
Настраивает итератор, чтобы он указывал на указанные
ndimиндексы, на которые указываетmulti_index. Возвращает ошибку, если не отслеживается многоиндекс, индексы находятся за пределами границ или итерация внутреннего цикла отключена.Возвращает
NPY_SUCCEEDилиNPY_FAIL.
-
int NpyIter_GotoIndex(NpyIter* iter, npy_intp index) -
Настраивает итератор, чтобы он указывал на указанный
indexиндекс. Если итератор был создан с флагомNPY_ITER_C_INDEX,index— индекс в порядке C, а если итератор был создан с флагомNPY_ITER_F_INDEX,index— индекс в порядке Fortran. Возвращает ошибку, если не отслеживается индекс, индекс находится за пределами границ или итерация внутреннего цикла отключена.Возвращает
NPY_SUCCEEDилиNPY_FAIL.
-
npy_intp NpyIter_GetIterSize(NpyIter* iter) -
Возвращает количество элементов, по которым происходит итерация. Это произведение всех измерений в форме. Когда отслеживается многоиндекс (и может быть вызван
NpyIter_RemoveAxis) размер может быть-1, чтобы указать, что итератор слишком большой. Такой итератор недействителен, но может стать действительным после вызоваNpyIter_RemoveAxis. Проверять этот случай не обязательно.
-
npy_intp NpyIter_GetIterIndex(NpyIter* iter) -
Получает
iterindexитератора, который является индексом, соответствующим порядку итерации итератора.
-
void NpyIter_GetIterIndexRange(NpyIter* iter, npy_intp* istart, npy_intp* iend) -
Получает
iterindexподдиапазон, по которому происходит итерация. ЕслиNPY_ITER_RANGEDне был указан, он всегда возвращает диапазон[0, NpyIter_IterSize(iter)).
-
int NpyIter_GotoIterIndex(NpyIter* iter, npy_intp iterindex) -
Настраивает итератор, чтобы он указывал на указанный
iterindexиндекс. IterIndex — это индекс, соответствующий порядку итерации итератора. Возвращает ошибку, еслиiterindexнаходится за пределами границ, буферизация включена или итерация внутреннего цикла отключена.Возвращает
NPY_SUCCEEDилиNPY_FAIL.
-
npy_bool NpyIter_HasDelayedBufAlloc(NpyIter* iter) -
Возвращает 1, если флаг
NPY_ITER_DELAY_BUFALLOCбыл передан в конструктор итератора, и ещё не был вызван ни один из функций Reset, в противном случае 0.
-
npy_bool NpyIter_HasExternalLoop(NpyIter* iter) -
Возвращает 1, если вызывающей функции необходимо обработать самый внутренний 1-мерный цикл, или 0, если итератор обрабатывает все циклы. Это контролируется флагом конструктора
NPY_ITER_EXTERNAL_LOOPилиNpyIter_EnableExternalLoop.
-
npy_bool NpyIter_HasMultiIndex(NpyIter* iter) -
Возвращает 1, если итератор был создан с флагом
NPY_ITER_MULTI_INDEX, в противном случае 0.
-
npy_bool NpyIter_HasIndex(NpyIter* iter) -
Возвращает 1, если итератор был создан с флагом
NPY_ITER_C_INDEXилиNPY_ITER_F_INDEX, в противном случае 0.
-
npy_bool NpyIter_RequiresBuffering(NpyIter* iter) -
Возвращает 1, если итератор требует буферизации, что происходит, когда операнду требуется преобразование или выравнивание, и поэтому его нельзя использовать непосредственно.
-
npy_bool NpyIter_IsBuffered(NpyIter* iter) -
Возвращает 1, если итератор был создан с флагом
NPY_ITER_BUFFERED, в противном случае 0.
-
npy_bool NpyIter_IsGrowInner(NpyIter* iter) -
Возвращает 1, если итератор был создан с флагом
NPY_ITER_GROWINNER, в противном случае 0.
-
npy_intp NpyIter_GetBufferSize(NpyIter* iter) -
Если итератор буферизован, возвращает размер используемого буфера, в противном случае возвращает 0.
-
int NpyIter_GetNDim(NpyIter* iter) -
Возвращает количество измерений, по которым происходит итерация. Если в конструкторе итератора не был запрошен многоиндекс, это значение может быть меньше, чем количество измерений в исходных объектах.
-
int NpyIter_GetNOp(NpyIter* iter) -
Возвращает количество операндов в итераторе.
Когда
NPY_ITER_USE_MASKNAиспользуется с операндом, к концу списка операндов в итераторе добавляется новый операнд для отслеживания маски NA этого операнда. Таким образом, это равно числу операндов конструкции плюс числу операндов, для которых был указан флагNPY_ITER_USE_MASKNA.
-
int NpyIter_GetFirstMaskNAOp(NpyIter* iter) -
Новое в версии 1.7.
Возвращает индекс первого операнда маски NA в массиве. Это значение равно количеству операндов, переданных в конструктор.
-
npy_intp* NpyIter_GetAxisStrideArray(NpyIter* iter, int axis) -
Получает массив шагов для указанной оси. Требуется, чтобы итератор отслеживал многоиндекс, и чтобы буферизация не была включена.
Это может быть использовано, когда вы хотите сопоставить оси операндов каким-либо образом, а затем удалить их с помощью
NpyIter_RemoveAxisдля обработки их вручную. Вызвав эту функцию перед удалением осей, вы можете получить шаги для ручной обработки.Возвращает
NULLпри ошибке.
-
int NpyIter_GetShape(NpyIter* iter, npy_intp* outshape) -
Возвращает форму вещания итератора в
outshape. Это можно вызвать только для итератора, который отслеживает многоиндекс.Возвращает
NPY_SUCCEEDилиNPY_FAIL.
-
PyArray_Descr** NpyIter_GetDescrArray(NpyIter* iter) -
Возвращает указатель на
nopтипы данных Descrs для объектов, по которым производится итерация. Результат указывает вiter, поэтому вызывающий код не получает никаких ссылок на Descrs.Этот указатель можно кэшировать перед циклом итерации, вызов
iternextне изменит его.
-
PyObject** NpyIter_GetOperandArray(NpyIter* iter) -
Возвращает указатель на
nopоперанды PyObjects, по которым производится итерация. Результат указывает вiter, поэтому вызывающий код не получает никаких ссылок на PyObjects.
-
npy_int8* NpyIter_GetMaskNAIndexArray(NpyIter* iter) -
Новое в версии 1.7.
Возвращает указатель на
nopиндексы, которые сопоставляют операнды построения сNPY_ITER_USE_MASKNAпомеченными их соответствующими операндами маски NA и наоборот. Для операндов, которые не были помеченыNPY_ITER_USE_MASKNA, этот массив содержит отрицательные значения.
-
PyObject* NpyIter_GetIterView(NpyIter* iter, npy_intp i) -
Возвращает ссылку на новый вид ndarray, который является представлением i-го объекта в массиве
NpyIter_GetOperandArray, чьи размеры и шаги соответствуют внутренней оптимизированной схеме итерации. Итерация по виду в порядке C эквивалентна порядку итерации итератора.Например, если итератор был создан с одним массивом в качестве входных данных, и было возможно переупорядочить все его оси, а затем свести их к одной итерации со смещениями, это вернет представление, которое является одномерным массивом.
-
void NpyIter_GetReadFlags(NpyIter* iter, char* outreadflags) -
Заполняет
nopфлаги. Устанавливаетoutreadflags[i]в 1, если изop[i]можно читать, и в 0, если нет.
-
void NpyIter_GetWriteFlags(NpyIter* iter, char* outwriteflags) -
Заполняет
nopфлаги. Устанавливаетoutwriteflags[i]в 1, если вop[i]можно записывать, и в 0, если нет.
-
int NpyIter_CreateCompatibleStrides(NpyIter* iter, npy_intp itemsize, npy_intp* outstrides) -
Строит набор шагов, которые совпадают с шагами выходного массива, созданного с использованием флага
NPY_ITER_ALLOCATE, где для op_axes был передан NULL. Это для данных, упакованных непрерывно, но не обязательно в порядке C или Fortran. Это следует использовать вместе сNpyIter_GetShapeиNpyIter_GetNDimс флагомNPY_ITER_MULTI_INDEX, переданным в конструктор.Пример использования этой функции — сопоставить форму и расположение итератора и добавить одну или несколько размерностей. Например, для генерации вектора на значение ввода для численного градиента вы передаете ndim*itemsize для itemsize, затем добавляете еще одну размерность в конец с размером ndim и шагом itemsize. Для матрицы Гессе вы делаете то же самое, но добавляете две размерности или используете симметрию и упаковываете ее в 1 размерность с определенной кодировкой.
Эта функция может быть вызвана только если итератор отслеживает многоиндекс и если
NPY_ITER_DONT_NEGATE_STRIDESбыл использован для предотвращения итерации оси в обратном порядке.Если массив создается этим методом, просто добавление ‘itemsize’ для каждой итерации пройдёт по новому массиву, соответствующему итератору.
Возвращает
NPY_SUCCEEDилиNPY_FAIL.
-
npy_bool NpyIter_IsFirstVisit(NpyIter* iter, int iop) -
Новое в версии 1.7.
Проверяет, является ли это первым посещением элементов указанного операнда сокращения, на котором указывает итератор. Функция возвращает разумный ответ для операндов сокращения и при отключенной буферизации. Ответ может быть некорректным для буферизованных не-операндов сокращения.
Эта функция предназначена для использования только в режиме EXTERNAL_LOOP, и даст неверные ответы, если этот режим не включен.
Если эта функция возвращает true, вызывающий код должен также проверить внутренний шаг операнда, потому что если этот шаг равен 0, то только первый элемент самого внутреннего внешнего цикла посещается впервые.
ПРЕДУПРЕЖДЕНИЕ: По соображениям производительности, ‘iop’ не проверяется на границах, не подтверждается, что ‘iop’ фактически является операндом сокращения, и не подтверждается, что режим EXTERNAL_LOOP включён. Эти проверки являются обязанностью вызывающего кода и должны выполняться вне любых внутренних циклов.
Функции для итерации
-
NpyIter_IterNextFunc* NpyIter_GetIterNext(NpyIter* iter, char** errmsg) -
Возвращает указатель на функцию для итерации. Специализированная версия указателя на функцию может быть вычислена этой функцией вместо хранения в структуре итератора. Таким образом, для хорошей производительности требуется, чтобы указатель на функцию был сохранён в переменной, а не извлекался для каждой итерации цикла.
Возвращает NULL при ошибке. Если errmsg не равен NULL, исключение Python не устанавливается, когда
NPY_FAILвозвращается. Вместо этого, *errmsg устанавливается в сообщение об ошибке. Когда errmsg не равен NULL, функцию можно безопасно вызывать без удержания блокировки Python GIL.Типичная структура цикла выглядит следующим образом.
NpyIter_IterNextFunc *iternext = NpyIter_GetIterNext(iter, NULL); char** dataptr = NpyIter_GetDataPtrArray(iter); do { /* use the addresses dataptr[0], ... dataptr[nop-1] */ } while(iternext(iter));Когда
NPY_ITER_EXTERNAL_LOOPуказан, типичная структура внутреннего цикла выглядит следующим образом.NpyIter_IterNextFunc *iternext = NpyIter_GetIterNext(iter, NULL); char** dataptr = NpyIter_GetDataPtrArray(iter); npy_intp* stride = NpyIter_GetInnerStrideArray(iter); npy_intp* size_ptr = NpyIter_GetInnerLoopSizePtr(iter), size; npy_intp iop, nop = NpyIter_GetNOp(iter); do { size = *size_ptr; while (size--) { /* use the addresses dataptr[0], ... dataptr[nop-1] */ for (iop = 0; iop < nop; ++iop) { dataptr[iop] += stride[iop]; } } } while (iternext());Обратите внимание, что мы используем массив dataptr внутри итератора, а не копируем значения во временную локальную переменную. Это возможно, потому что когда
iternext()вызывается, эти указатели будут перезаписаны свежими значениями, а не инкрементально обновлены.Если используется буфер фиксированного размера во время компиляции (оба флага
NPY_ITER_BUFFEREDиNPY_ITER_EXTERNAL_LOOP), внутренний размер также может использоваться в качестве сигнала. Размер гарантированно становится нулём, когдаiternext()возвращает false, что позволяет использовать следующую структуру цикла. Обратите внимание, что если вы используете эту структуру, вы не должны передаватьNPY_ITER_GROWINNERв качестве флага, потому что это приведёт к увеличению размеров в некоторых случаях./* The constructor should have buffersize passed as this value */ #define FIXED_BUFFER_SIZE 1024 NpyIter_IterNextFunc *iternext = NpyIter_GetIterNext(iter, NULL); char **dataptr = NpyIter_GetDataPtrArray(iter); npy_intp *stride = NpyIter_GetInnerStrideArray(iter); npy_intp *size_ptr = NpyIter_GetInnerLoopSizePtr(iter), size; npy_intp i, iop, nop = NpyIter_GetNOp(iter); /* One loop with a fixed inner size */ size = *size_ptr; while (size == FIXED_BUFFER_SIZE) { /* * This loop could be manually unrolled by a factor * which divides into FIXED_BUFFER_SIZE */ for (i = 0; i < FIXED_BUFFER_SIZE; ++i) { /* use the addresses dataptr[0], ... dataptr[nop-1] */ for (iop = 0; iop < nop; ++iop) { dataptr[iop] += stride[iop]; } } iternext(); size = *size_ptr; } /* Finish-up loop with variable inner size */ if (size > 0) do { size = *size_ptr; while (size--) { /* use the addresses dataptr[0], ... dataptr[nop-1] */ for (iop = 0; iop < nop; ++iop) { dataptr[iop] += stride[iop]; } } } while (iternext());
-
NpyIter_GetMultiIndexFunc *NpyIter_GetGetMultiIndex(NpyIter* iter, char** errmsg) -
Возвращает указатель на функцию для получения текущего многоиндекса итератора. Возвращает NULL, если итератор не отслеживает многоиндекс. Рекомендуется кэшировать этот указатель на функцию в локальной переменной перед циклом итерации.
Возвращает NULL при ошибке. Если errmsg не равен NULL, исключение Python не устанавливается, когда
NPY_FAILвозвращается. Вместо этого, *errmsg устанавливается в сообщение об ошибке. Когда errmsg не равен NULL, функцию можно безопасно вызывать без удержания блокировки Python GIL.
-
char** NpyIter_GetDataPtrArray(NpyIter* iter) -
Возвращает указатель на
nopуказатели на данные. ЕслиNPY_ITER_EXTERNAL_LOOPне был указан, каждый указатель данных указывает на текущий элемент данных итератора. Если внутренняя итерация не указана, он указывает на первый элемент данных внутреннего цикла.Этот указатель можно кэшировать перед циклом итерации, вызов
iternextне изменит его. Эту функцию можно безопасно вызывать без удержания блокировки Python GIL.
-
char** NpyIter_GetInitialDataPtrArray(NpyIter* iter) -
Получает массив указателей на данные непосредственно в массивы (никогда не в буферы), соответствующие индексу итерации 0.
Эти указатели отличаются от указателей, принятых
NpyIter_ResetBasePointers, так как направление вдоль некоторых осей может быть обращено.Эту функцию можно безопасно вызывать без удержания блокировки Python GIL.
-
npy_intp* NpyIter_GetIndexPtr(NpyIter* iter) -
Возвращает указатель на отслеживаемый индекс или NULL, если индекс не отслеживается. Он пригоден только в том случае, если один из флагов
NPY_ITER_C_INDEXилиNPY_ITER_F_INDEXбыл указан во время построения.
Когда используется флаг NPY_ITER_EXTERNAL_LOOP, коду необходимо знать параметры для выполнения внутреннего цикла. Эти функции предоставляют эту информацию.
-
npy_intp* NpyIter_GetInnerStrideArray(NpyIter* iter) -
Возвращает указатель на массив
nopшагов, по одному для каждого итерируемого объекта, для использования внутренним циклом.Этот указатель можно кэшировать перед циклом итерации, вызов
iternextне изменит его. Эту функцию можно безопасно вызывать без удержания блокировки Python GIL.ПРЕДУПРЕЖДЕНИЕ: Хотя указатель можно кэшировать, его значения могут измениться, если итератор буферизован.
-
npy_intp* NpyIter_GetInnerLoopSizePtr(NpyIter* iter) -
Возвращает указатель на количество итераций, которое должно выполнить внутреннее цикл.
Этот адрес может быть кэширован перед циклом итераций, вызов
iternextне изменит его. Само значение может изменяться во время итерации, особенно если включена буферизация. Данную функцию можно безопасно вызывать без блокировки Python GIL.
-
void NpyIter_GetInnerFixedStrideArray(NpyIter* iter, npy_intp* out_strides) -
Получает массив шагов, которые являются фиксированными или не будут меняться в течение всей итерации. Для шагов, которые могут измениться, значение NPY_MAX_INTP помещается в шаг.
После того, как итератор подготовлен к итерации (после сброса, если использовался
NPY_DELAY_BUFALLOC), вызовите эту функцию, чтобы получить шаги, которые могут быть использованы для выбора быстрой функции внутреннего цикла. Например, если шаг равен 0, это означает, что внутренний цикл всегда может загрузить значение в переменную один раз, а затем использовать переменную на протяжении всего цикла, или если шаг равен размеру элемента, можно использовать контигуумную версию для этого операнда.Данную функцию можно безопасно вызывать без блокировки Python GIL.
Преобразование из предыдущих итераторов NumPy
Старый API итераторов включает функции, такие как PyArrayIter_Check, PyArray_Iter* и PyArray_ITER_*. Массив многоитератора включает PyArray_MultiIter*, PyArray_Broadcast и PyArray_RemoveSmallest. Новый дизайн итератора заменяет всю эту функциональность одним объектом и соответствующим API. Одна из целей нового API заключается в том, что все использования существующего итератора могут быть заменены новым итератором без значительных усилий. В версии 1.6 основное исключение из этого правила — итератор окрестностей, у которого нет соответствующих функций в этом итераторе.
Вот таблица преобразования, в которой указаны функции, используемые с новым итератором:
© 2008–2017 NumPy Developers
Licensed under the NumPy License.
https://docs.scipy.org/doc/numpy-1.12.0/reference/c-api.iterator.html