Spec-Zone.ru › NumPy 1.18

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

После выделения с этим флагом вызывающий код может получить новый массив, вызвав 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_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 const* itershape, npy_intp buffersize)

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

Если значения -1/NULL передаются в oa_ndim, op_axes, itershape, и buffersize, это эквивалентно NpyIter_MultiNew.

Параметр oa_ndim, если он не равен 0 или -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 равно 0, используется размер буфера по умолчанию, в противном случае он определяет размер буфера.

Возвращает 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 исключения.

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

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)

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

Возвращает 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 const* multi_index)

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

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

int NpyIter_GotoIndex(NpyIter* iter, npy_intp 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.

END_OF_DOCUMENT_MARKER
npy_bool NpyIter_HasDelayedBufAlloc(NpyIter* iter)

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

npy_bool NpyIter_HasExternalLoop(NpyIter* iter)

Возвращает 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_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.

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.

END_OF_DOCUMENT_MARKER
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 основное исключение из этого правила — итератор окрестностей, у которого нет соответствующих функций в этом итераторе.

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

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

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–2020 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/1.18/reference/c-api/iterator.html

Spec-Zone.ru

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