Spec-Zone.ru › NumPy 1.21

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_uint32flags, NPY_ORDERorder, NPY_CASTINGcasting, 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_intpnop, PyArrayObject**op, npy_uint32flags, NPY_ORDERorder, NPY_CASTINGcasting, 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

Это влияет только на итератор, когда для параметра order указано 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

Указывает, что массивы с размером нуль должны быть разрешены. Поскольку типичный цикл итерации естественным образом не работает с массивами нулевого размера, вы должны проверить, что IterSize больше нуля, прежде чем войти в цикл итерации. В настоящее время проверяются только операнды, а не принудительная форма.

NPY_ITER_REDUCE_OK

Разрешает записи операнды с размерностью с нулевым шагом и размером больше единицы. Обратите внимание, что такие операнды должны быть для чтения/записи.

При включённой буферизации это также переключает режим буферизации, который уменьшает длину цикла по мере необходимости, чтобы не перезаписывать значения, которые уменьшаются.

Обратите внимание, что если вы хотите выполнить уменьшение на автоматически выделенном выводе, вы должны использовать 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 для выделения и заполнения буферов их начальными значениями.

NPY_ITER_COPY_IF_OVERLAP

Если любой операнд записи имеет перекрытие с любым операндом чтения, устраните все перекрытия, создав временные копии (включив UPDATEIFCOPY для операндов записи, если необходимо). Пара операндов перекрывается, если существует адрес памяти, содержащий данные, общие для обоих массивов.

Поскольку точное обнаружение перекрытий имеет экспоненциальное время выполнения по количеству измерений, решение принимается на основе эвристик, что приводит к ложным срабатываниям (ненужные копии в необычных случаях), но без ложных отрицаний.

Если существует какое-либо перекрытие чтения/записи, этот флаг гарантирует, что результат операции будет таким же, как если бы все операнды были скопированы. В тех случаях, когда потребуются копии, результат вычисления может быть неопределённым без этого флага!

Флаги, которые могут быть переданы в op_flags[i], где 0 <= i < nop:

NPY_ITER_READWRITE
NPY_ITER_READONLY
NPY_ITER_WRITEONLY

Указывает, как пользователь итератора будет читать или записывать в op[i]. Для каждого операнда должен быть указан ровно один из этих флагов. Использование NPY_ITER_READWRITE или NPY_ITER_WRITEONLY для предоставленного пользователем операнда может вызвать семантику WRITEBACKIFCOPY`. Данные будут записаны обратно в исходный массив при вызове NpyIter_Deallocate.

NPY_ITER_COPY

Разрешить копирование op[i], если он не соответствует требованиям типа данных или выравнивания, как указано в флагах и параметрах конструктора.

NPY_ITER_UPDATEIFCOPY

Вызывает NPY_ITER_COPY, и когда операнд массива помечен для записи и скопирован, данные в копии копируются обратно в op[i] при вызове NpyIter_Deallocate.

Если операнд помечен как только для записи и требуется копия, будет создан неинициализированный временный массив, а затем скопирован обратно в op[i] при вызове NpyIter_Deallocate, вместо выполнения ненужной операции копирования.

NPY_ITER_NBO
NPY_ITER_ALIGNED
NPY_ITER_CONTIG

Заставляет итератор предоставлять данные для op[i], которые находятся в родном байтовом порядке, выровнены в соответствии с требованиями dtype, непрерывны или являются любой комбинацией.

По умолчанию итератор генерирует указатели в предоставленные массивы, которые могут быть выровнены или не выровнены, и иметь любой байтовый порядок. Если копирование или буферизация не включены, и данные операнда не удовлетворяют ограничениям, будет выдано сообщение об ошибке.

Ограничение непрерывности применяется только к внутреннему циклу, последующие внутренние циклы могут иметь произвольные изменения указателей.

Если запрашиваемый тип данных находится в неродном байтовом порядке, флаг 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, код, выполняющий итерацию, может записывать в этот операнд для управления тем, какие элементы останутся нетронутыми, а какие будут изменены. Это полезно, когда маска должна быть комбинацией входных масок.

NPY_ITER_WRITEMASKED

Добавлена в версии 1.7.

Этот массив является маской для всех writemasked операндов. Код использует флаг writemasked, который указывает, что только элементы, где выбранный операнд ARRAYMASK равен True, будут записаны. В общем случае итератор не навязывает это, ответственность за соблюдение этого условия лежит на коде, выполняющем итерацию.

Когда используется флаг writemasked, и этот операнд буферизован, это меняет способ копирования данных из буфера в массив. Используется процедура маскированной копии, которая копирует только элементы в буфере, для которых writemasked возвращает true от соответствующего элемента в операнде ARRAYMASK.

NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE

При проверке перекрытия в памяти предполагается, что к операндам с включённым NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE обращаются только в порядке итератора.

Это позволяет итератору рассуждать о зависимости данных, возможно, избегая ненужных копий.

Этот флаг действует только если NPY_ITER_COPY_IF_OVERLAP включён в итераторе.

NpyIter*NpyIter_AdvancedNew(npy_intpnop, PyArrayObject**op, npy_uint32flags, NPY_ORDERorder, NPY_CASTINGcasting, npy_uint32*op_flags, PyArray_Descr**op_dtypes, intoa_ndim, int**op_axes, npy_intpconst*itershape, npy_intpbuffersize)

Расширяет 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 и итерирует по всему диапазону.

При использовании итератора в многопоточных кодах или в кодах, не держащих GIL Python, следует позаботиться о том, чтобы вызывать только безопасные в этом контексте функции. NpyIter_Copy нельзя безопасно вызывать без Python GIL, так как она увеличивает Python-ссылки. Reset* и некоторые другие функции могут безопасно вызываться, передавая параметр errmsg как не-NULL, чтобы функции возвращали ошибки через него вместо установки исключения Python.

NpyIter_Deallocate должен быть вызван для каждой копии.

intNpyIter_RemoveAxis(NpyIter*iter, intaxis)

Удаляет ось из итерации. Это требует, чтобы NPY_ITER_MULTI_INDEX было установлено при создании итератора, и не работает, если буферизация включена или отслеживается индекс. Эта функция также сбрасывает итератор в исходное состояние.

Это полезно для настройки цикла накопления, например. Итератор сначала может быть создан со всеми измерениями, включая ось накопления, чтобы выход был создан правильно. Затем ось накопления может быть удалена, и вычисление выполнено вложенным образом.

ПРЕДУПРЕЖДЕНИЕ: Эта функция может изменить внутреннюю структуру памяти итератора. Любые кэшированные функции или указатели итератора должны быть получены заново! Диапазон итератора также будет сброшен.

Возвращает NPY_SUCCEED или NPY_FAIL.

intNpyIter_RemoveMultiIndex(NpyIter*iter)

Если итератор отслеживает многоиндекс, это устраняет поддержку многоиндексов и выполняет дальнейшие оптимизации итератора, возможные если многоиндексы не нужны. Эта функция также сбрасывает итератор в исходное состояние.

ПРЕДУПРЕЖДЕНИЕ: Эта функция может изменить внутреннюю структуру памяти итератора. Любые кэшированные функции или указатели итератора должны быть получены заново!

После вызова этой функции NpyIter_HasMultiIndex(iter) вернёт false.

Возвращает NPY_SUCCEED или NPY_FAIL.

intNpyIter_EnableExternalLoop(NpyIter*iter)

Если был вызван NpyIter_RemoveMultiIndex, вы можете включить флаг NPY_ITER_EXTERNAL_LOOP. Этот флаг не разрешен совместно с NPY_ITER_MULTI_INDEX, поэтому эта функция предназначена для включения этой функции после вызова NpyIter_RemoveMultiIndex. Эта функция также сбрасывает итератор в исходное состояние.

ПРЕДУПРЕЖДЕНИЕ: Эта функция изменяет внутреннюю логику итератора. Любые кэшированные функции или указатели итератора должны быть получены заново!

Возвращает NPY_SUCCEED или NPY_FAIL.

intNpyIter_Deallocate(NpyIter*iter)

Освобождает объект итератора и выполняет необходимые записи обратно.

Возвращает NPY_SUCCEED или NPY_FAIL.

intNpyIter_Reset(NpyIter*iter, char**errmsg)

Сбрасывает итератор в исходное состояние, в начало диапазона итерации.

Возвращает NPY_SUCCEED или NPY_FAIL. Если errmsg не равен NULL, то при возвращении NPY_FAIL не возникает исключение Python. Вместо этого *errmsg устанавливается в сообщение об ошибке. Когда errmsg не равен NULL, функцию можно безопасно вызывать без удержания блокировки Python GIL.

intNpyIter_ResetToIterIndexRange(NpyIter*iter, npy_intpistart, npy_intpiend, 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, то при возвращении NPY_FAIL не возникает исключение Python. Вместо этого *errmsg устанавливается в сообщение об ошибке. Когда errmsg не равен NULL, функцию можно безопасно вызывать без удержания блокировки Python GIL.

intNpyIter_ResetBasePointers(NpyIter*iter, char**baseptrs, char**errmsg)

Сбрасывает итератор в исходное состояние, но использует значения в baseptrs для данных вместо указателей из итерируемых массивов. Эта функция предназначена для использования вместе с параметром op_axes в коде вложенной итерации с двумя или более итераторами.

Возвращает NPY_SUCCEED или NPY_FAIL. Если errmsg не равен NULL, то при возвращении NPY_FAIL не возникает исключение Python. Вместо этого *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));
intNpyIter_GotoMultiIndex(NpyIter*iter, npy_intpconst*multi_index)

Настраивает итератор на указанные индексы, на которые указывает ndim. Возвращает ошибку, если не отслеживается многомерный индекс, индексы находятся за пределами границ или итерация внутреннего цикла отключена.

Возвращает NPY_SUCCEED или NPY_FAIL.

intNpyIter_GotoIndex(NpyIter*iter, npy_intpindex)

Настраивает итератор на указанный index. Если итератор был создан с флагом NPY_ITER_C_INDEX, index — индекс в порядке C, а если итератор был создан с флагом NPY_ITER_F_INDEX, index — индекс в порядке Fortran. Возвращает ошибку, если не отслеживается индекс, индекс находится за пределами границ или итерация внутреннего цикла отключена.

Возвращает NPY_SUCCEED или NPY_FAIL.

npy_intpNpyIter_GetIterSize(NpyIter*iter)

Возвращает количество итерируемых элементов. Это произведение всех размеров в форме. Когда отслеживается многомерный индекс (и может быть вызван NpyIter_RemoveAxis), размер может быть -1 для обозначения того, что итератор слишком большой. Такой итератор недействителен, но может стать действительным после вызова NpyIter_RemoveAxis. Проверка этого случая не требуется.

npy_intpNpyIter_GetIterIndex(NpyIter*iter)

Получает iterindex итератора, который является индексом, соответствующим порядку итерации итератора.

voidNpyIter_GetIterIndexRange(NpyIter*iter, npy_intp*istart, npy_intp*iend)

Получает iterindex поддиапазон, который итерируется. Если NPY_ITER_RANGED не был указан, возвращает диапазон [0, NpyIter_IterSize(iter)).

intNpyIter_GotoIterIndex(NpyIter*iter, npy_intpiterindex)

Настраивает итератор на указанный iterindex индекс. IterIndex — это индекс, соответствующий порядку итерации итератора. Возвращает ошибку, если iterindex находится за пределами границ, буферизация включена или итерация внутреннего цикла отключена.

Возвращает NPY_SUCCEED или NPY_FAIL.

npy_boolNpyIter_HasDelayedBufAlloc(NpyIter*iter)

Возвращает 1, если флаг NPY_ITER_DELAY_BUFALLOC был передан в конструктор итератора и еще не был выполнен ни один из функций Reset, в противном случае — 0.

END_OF_DOCUMENT_MARKER
npy_boolNpyIter_HasExternalLoop(NpyIter*iter)

Возвращает 1, если вызывающей стороне нужно обработать вложенный 1-мерный цикл, или 0, если итератор обрабатывает все циклы. Это контролируется флагом конструктора NPY_ITER_EXTERNAL_LOOP или NpyIter_EnableExternalLoop.

npy_boolNpyIter_HasMultiIndex(NpyIter*iter)

Возвращает 1, если итератор был создан с флагом NPY_ITER_MULTI_INDEX, 0 в противном случае.

npy_boolNpyIter_HasIndex(NpyIter*iter)

Возвращает 1, если итератор был создан с флагом NPY_ITER_C_INDEX или NPY_ITER_F_INDEX, 0 в противном случае.

npy_boolNpyIter_RequiresBuffering(NpyIter*iter)

Возвращает 1, если итератор требует буферизации, что происходит, когда операнду требуется преобразование или выравнивание, и поэтому он не может быть использован напрямую.

npy_boolNpyIter_IsBuffered(NpyIter*iter)

Возвращает 1, если итератор был создан с флагом NPY_ITER_BUFFERED, 0 в противном случае.

npy_boolNpyIter_IsGrowInner(NpyIter*iter)

Возвращает 1, если итератор был создан с флагом NPY_ITER_GROWINNER, 0 в противном случае.

npy_intpNpyIter_GetBufferSize(NpyIter*iter)

Если итератор буферизован, возвращает размер используемого буфера, в противном случае возвращает 0.

intNpyIter_GetNDim(NpyIter*iter)

Возвращает количество измерений, по которым производится итерация. Если в конструкторе итератора не был запрошен многомерный индекс, это значение может быть меньше количества измерений в исходных объектах.

intNpyIter_GetNOp(NpyIter*iter)

Возвращает количество операндов в итераторе.

npy_intp*NpyIter_GetAxisStrideArray(NpyIter*iter, intaxis)

Получает массив сдвигов для указанной оси. Требует, чтобы итератор отслеживал многомерный индекс и чтобы буферизация не была включена.

Это может быть использовано, когда вы хотите каким-то образом согласовать оси операндов, а затем удалить их с помощью NpyIter_RemoveAxis, чтобы обработать их вручную. Вызвав эту функцию до удаления осей, вы можете получить сдвиги для ручной обработки.

Возвращает NULL при ошибке.

intNpyIter_GetShape(NpyIter*iter, npy_intp*outshape)

Возвращает форму трансляции итератора в outshape. Это можно вызывать только для итератора, который отслеживает многомерный индекс.

Возвращает NPY_SUCCEED или NPY_FAIL.

PyArray_Descr**NpyIter_GetDescrArray(NpyIter*iter)

Возвращает указатель на массивы описателей типов данных nop итерации объектов. Результат указывает на iter, поэтому вызывающая сторона не получает никаких ссылок на описатели.

Этот указатель можно кэшировать перед циклом итерации; вызов iternext не изменит его.

PyObject**NpyIter_GetOperandArray(NpyIter*iter)

Возвращает указатель на массив операндов nop PyObjects, по которым происходит итерация. Результат указывает на iter, поэтому вызывающая сторона не получает никаких ссылок на PyObjects.

PyObject*NpyIter_GetIterView(NpyIter*iter, npy_intpi)

Возвращает ссылку на новый вид ndarray, который является представлением i-го объекта в массиве NpyIter_GetOperandArray, чьи размеры и сдвиги соответствуют внутренней оптимизированной схеме итерации. Итерация вида C-порядка эквивалентна порядку итерации итератора.

Например, если итератор был создан с одним массивом в качестве входных данных и было возможно переупорядочить все его оси, а затем свернуть их в одну итерацию со сдвигом, это вернёт представление, являющееся одномерным массивом.

voidNpyIter_GetReadFlags(NpyIter*iter, char*outreadflags)

Заполняет nop флаги. Устанавливает outreadflags[i] в 1, если op[i] можно считать, и в 0, если нет.

END_OF_DOCUMENT_MARKER
voidNpyIter_GetWriteFlags(NpyIter*iter, char*outwriteflags)

Заполняет nop флаги. Устанавливает outwriteflags[i] в 1, если в op[i] можно записать, и в 0, если нет.

intNpyIter_CreateCompatibleStrides(NpyIter*iter, npy_intpitemsize, npy_intp*outstrides)

Создаёт набор шагов, совпадающих с шагами выходного массива, созданного с использованием флага NPY_ITER_ALLOCATE, где для op_axes было передано значение NULL. Это для данных, упакованных непрерывно, но необязательно в порядке C или Fortran. Это следует использовать вместе с NpyIter_GetShape и NpyIter_GetNDim с флагом NPY_ITER_MULTI_INDEX , переданным в конструктор.

Пример использования этой функции — соответствие формы и структуры итератора и добавление одного или нескольких измерений. Например, для генерации вектора на каждый входной параметр для числового градиента передайте ndim*itemsize для itemsize, а затем добавьте другое измерение в конец с размером ndim и шагом itemsize. Для построения матрицы Гессе сделайте то же самое, но добавьте два измерения или воспользуйтесь симметрией и упакуйте её в одно измерение с определённым кодированием.

Эта функция может вызываться только в том случае, если итератор отслеживает многоиндекс и если NPY_ITER_DONT_NEGATE_STRIDES было использовано для предотвращения итерации оси в обратном порядке.

Если массив создан с помощью этого метода, просто добавление ‘itemsize’ для каждой итерации пройдёт по новому массиву, соответствуя итератору.

Возвращает NPY_SUCCEED или NPY_FAIL.

npy_boolNpyIter_IsFirstVisit(NpyIter*iter, intiop)

Новое в версии 1.7.

Проверяет, является ли это первый визит к элементам указанного операнда сокращения, на который указывает итератор. Функция возвращает разумный ответ для операндов сокращения и при отключённом буферировании. Ответ может быть неверным для не-операндов сокращения с буферизацией.

Эта функция предназначена для использования только в режиме EXTERNAL_LOOP и может давать неверные ответы, если этот режим не включён.

Если функция возвращает true, вызывающий код должен также проверить шаг внутреннего цикла операнда, потому что если этот шаг равен 0, то только первый элемент самого внутреннего внешнего цикла посещается впервые.

ВНИМАНИЕ: по соображениям производительности ‘iop’ не проверяется на границы, не подтверждается, что ‘iop’ фактически является операндом сокращения, и не подтверждается, что режим EXTERNAL_LOOP включён. Эти проверки являются обязанностью вызывающего кода и должны выполняться вне любых внутренних циклов.

Функции для итерации

NpyIter_IterNextFunc*NpyIter_GetIterNext(NpyIter*iter, char**errmsg)

Возвращает указатель на функцию для итерации. Специализированная версия указателя на функцию может быть рассчитана этой функцией вместо хранения в структуре итератора. Таким образом, для хорошей производительности требуется, чтобы указатель на функцию был сохранён в переменной, а не извлекался для каждой итерации цикла.

Возвращает NULL, если произошла ошибка. Если errmsg не равен NULL, при возврате NPY_FAIL исключение Python не устанавливается. Вместо этого *errmsg устанавливается в сообщение об ошибке. Когда errmsg не равен NULL, функцию можно безопасно вызывать без блокировки интерпретатора Python.

Типичная конструкция цикла:

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, при возврате NPY_FAIL исключение Python не устанавливается. Вместо этого *errmsg устанавливается в сообщение об ошибке. Когда errmsg не равен NULL, функцию можно безопасно вызывать без блокировки интерпретатора Python.

char**NpyIter_GetDataPtrArray(NpyIter*iter)

Возвращает указатель на массив nop указателей на данные. Если NPY_ITER_EXTERNAL_LOOP не был указан, каждый указатель данных указывает на текущий элемент данных итератора. Если внутренняя итерация не была указана, он указывает на первый элемент данных внутреннего цикла.

Этот указатель можно кэшировать перед циклом итерации, вызов iternext не изменит его. Функцию можно безопасно вызывать без блокировки интерпретатора Python.

char**NpyIter_GetInitialDataPtrArray(NpyIter*iter)

Получает массив указателей данных непосредственно в массивы (никогда не в буферы), соответствующие индексу итерации 0.

Эти указатели отличаются от указателей, принимаемых NpyIter_ResetBasePointers, так как направление вдоль некоторых осей может быть обращено.

Функцию можно безопасно вызывать без блокировки интерпретатора Python.

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.

ВНИМАНИЕ: хотя указатель можно кэшировать, его значения могут измениться, если итератор буферизуется.

END_OF_DOCUMENT_MARKER
npy_intp*NpyIter_GetInnerLoopSizePtr(NpyIter*iter)

Возвращает указатель на количество итераций, которое должно выполнить внутреннее цикл.

Этот адрес может быть кэширован перед циклом итерации, вызов iternext его не изменит. Само значение может измениться во время итерации, особенно если включена буферизация. Данную функцию можно безопасно вызывать без блокировки интерпретатора Python.

voidNpyIter_GetInnerFixedStrideArray(NpyIter*iter, npy_intp*out_strides)

Получает массив шагов, которые являются фиксированными или не будут изменяться на протяжении всей итерации. Для шагов, которые могут изменяться, в массив шагов записывается значение NPY_MAX_INTP.

После подготовки итератора к итерации (после сброса, если NPY_ITER_DELAY_BUFALLOC использовался), вызовите эту функцию, чтобы получить шаги, которые могут быть использованы для выбора быстрой внутренней функции цикла. Например, если шаг равен 0, это означает, что внутренний цикл может всегда загружать своё значение в переменную один раз, а затем использовать переменную на протяжении всего цикла, или если шаг равен размеру элемента, может быть использована контигуальная версия для этого операнда.

Данную функцию можно безопасно вызывать без блокировки интерпретатора Python.

Преобразование из предыдущих итераторов NumPy

Старый API итераторов включает функции, такие как PyArrayIter_Check, PyArray_Iter* и PyArray_ITER_*. Многомерный массив итераторов включает PyArray_MultiIter*, PyArray_Broadcast и PyArray_RemoveSmallest. Новый дизайн итераторов заменяет все эти функции одним объектом и соответствующим API. Одна из целей нового API заключается в том, что все использования существующих итераторов должны быть заменимы новым итератором без значительных усилий. В версии 1.6 основным исключением из этого правила является итератор окрестностей, который не имеет соответствующих функций в этом итераторе.

Вот таблица преобразования, показывающая, какие функции следует использовать с новым итератором:

Функции итераторов

PyArray_IterNew

NpyIter_New

PyArray_IterAllButAxis

NpyIter_New + параметр axes или флаг итератора NPY_ITER_EXTERNAL_LOOP

PyArray_BroadcastToShape

НЕ ПОДДЕРЖИВАЕТСЯ (Используйте поддержку нескольких операндов вместо этого.)

PyArrayIter_Check

Необходимо добавить в экспозицию Python

PyArray_ITER_RESET

NpyIter_Reset

PyArray_ITER_NEXT

Указатель на функцию из NpyIter_GetIterNext

PyArray_ITER_DATA

NpyIter_GetDataPtrArray

PyArray_ITER_GOTO

NpyIter_GotoMultiIndex

PyArray_ITER_GOTO1D

NpyIter_GotoIndex или NpyIter_GotoIterIndex

PyArray_ITER_NOTDONE

Возвращаемое значение указателя на функцию iternext

Функции многомерных итераторов

PyArray_MultiIterNew

NpyIter_MultiNew

PyArray_MultiIter_RESET

NpyIter_Reset

PyArray_MultiIter_NEXT

Указатель на функцию из NpyIter_GetIterNext

PyArray_MultiIter_DATA

NpyIter_GetDataPtrArray

PyArray_MultiIter_NEXTi

НЕ ПОДДЕРЖИВАЕТСЯ (всегда итерация по шагам)

PyArray_MultiIter_GOTO

NpyIter_GotoMultiIndex

PyArray_MultiIter_GOTO1D

NpyIter_GotoIndex или NpyIter_GotoIterIndex

PyArray_MultiIter_NOTDONE

Возвращаемое значение указателя на функцию iternext

PyArray_Broadcast

Обрабатывается NpyIter_MultiNew

PyArray_RemoveSmallest

Флаг итератора NPY_ITER_EXTERNAL_LOOP

Другие функции

PyArray_ConvertToCommonType

Флаг итератора NPY_ITER_COMMON_DTYPE

© 2005–2022 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.21/reference/c-api/iterator.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API