API итератора массивов#

Итератор массива#

Итератор массива инкапсулирует многие ключевые особенности 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;
}

Пример отслеживания мультииндекса#

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

int PrintMultiIndex(PyArrayObject *arr) {
    NpyIter *iter;
    NpyIter_IterNextFunc *iternext;
    npy_intp multi_index[2];

    iter = NpyIter_New(
        arr, NPY_ITER_READONLY | NPY_ITER_MULTI_INDEX | NPY_ITER_REFS_OK,
        NPY_KEEPORDER, NPY_NO_CASTING, NULL);
    if (iter == NULL) {
        return -1;
    }
    if (NpyIter_GetNDim(iter) != 2) {
        NpyIter_Deallocate(iter);
        PyErr_SetString(PyExc_ValueError, "Array must be 2-D");
        return -1;
    }
    if (NpyIter_GetIterSize(iter) != 0) {
        iternext = NpyIter_GetIterNext(iter, NULL);
        if (iternext == NULL) {
            NpyIter_Deallocate(iter);
            return -1;
        }
        NpyIter_GetMultiIndexFunc *get_multi_index =
            NpyIter_GetGetMultiIndex(iter, NULL);
        if (get_multi_index == NULL) {
            NpyIter_Deallocate(iter);
            return -1;
        }

        do {
            get_multi_index(iter, multi_index);
            printf("multi_index is [%" NPY_INTP_FMT ", %" NPY_INTP_FMT "]\n",
                   multi_index[0], multi_index[1]);
        } while (iternext(iter));
    }
    if (!NpyIter_Deallocate(iter)) {
        return -1;
    }
    return 0;
}

При вызове с массивом 2x3, приведённый выше пример выводит:

multi_index is [0, 0]
multi_index is [0, 1]
multi_index is [0, 2]
multi_index is [1, 0]
multi_index is [1, 1]
multi_index is [1, 2]

Типы данных итератора#

Структура итератора является внутренней деталью, и пользовательский код видит только неполную структуру.

тип 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 флаги, NPY_ORDER порядок, NPY_CASTING приведение типов, PyArray_Descr *dtype)#

Создает итератор для данного объекта массива numpy op.

Флаги, которые могут быть переданы в flags являются любой комбинацией глобальных и пооперандных флагов, задокументированных в NpyIter_MultiNew, за исключением NPY_ITER_ALLOCATE.

Любой из NPY_ORDER значения enum могут быть переданы в order. Для эффективной итерации, NPY_KEEPORDER является лучшим вариантом, а другие порядки навязывают конкретный шаблон итерации.

Любой из NPY_CASTING значения enum могут быть переданы в 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 флаги, NPY_ORDER порядок, NPY_CASTING приведение типов, npy_uint32 *op_flags, PyArray_Descr **op_dtypes)#

Создает итератор для трансляции nop объекты массивов, предоставленные в op, используя стандартные правила вещания NumPy.

Любой из NPY_ORDER значения enum могут быть переданы в order. Для эффективной итерации, NPY_KEEPORDER является лучшим вариантом, а другие порядки обеспечивают определённый шаблон итерации. При использовании NPY_KEEPORDER, если вы также хотите убедиться, что итерация не обращена вдоль оси, вы должны передать флаг NPY_ITER_DONT_NEGATE_STRIDES.

Любой из NPY_CASTING значения enum могут быть переданы в 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 указан для параметра order. По умолчанию с NPY_KEEPORDER, итератор меняет направление осей, которые имеют отрицательные шаги, чтобы память обходилась в прямом направлении. Этот флаг отключает этот шаг. Используйте этот флаг, если вы хотите использовать базовый порядок памяти осей, но не хотите менять направление оси. Это поведение numpy.ravel(a, order='K'), например.

NPY_ITER_COMMON_DTYPE#

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

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

NPY_ITER_REFS_OK#

Указывает, что массивы с ссылочными типами (массивы объектов или структурированные массивы, содержащие объектный тип) могут быть приняты и использованы в итераторе. Если этот флаг включен, вызывающая сторона должна быть уверена, что проверила, является ли NpyIter_IterationNeedsAPI(iter) истинно, в этом случае он может не освобождать GIL во время итерации. Если вы работаете с известными типами данных NpyIter_GetTransferFlags является более быстрым и точным способом проверки необходимости итератору API из-за буферизации.

NPY_ITER_ZEROSIZE_OK#

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

NPY_ITER_REDUCE_OK#

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

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

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

NPY_ITER_RANGED#

Включает поддержку итерации поддиапазонов полного iterindex range [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] который находится в собственном порядке байтов, выровнен в соответствии с требованиями типа данных, непрерывен или любое сочетание.

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

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

Если запрошенный тип данных имеет порядок байтов, отличный от нативного, флаг NBO переопределяет его, и запрошенный тип данных преобразуется в нативный порядок байтов.

NPY_ITER_ALLOCATE#

Это для выходных массивов и требует, чтобы флаг NPY_ITER_WRITEONLY или NPY_ITER_READWRITE должен быть установлен. Если op[i] равен NULL, создаёт новый массив с финальными размерностями вещания и макетом, соответствующим порядку итерации итератора.

Когда op[i] равен NULL, запрошенный тип данных op_dtypes[i] может быть NULL, в этом случае он автоматически генерируется из dtypes массивов, которые помечены как читаемые. Правила генерации dtype такие же, как для UFuncs. Особое внимание уделяется обработке порядка байтов в выбранном dtype. Если есть ровно один вход, dtype входа используется как есть. В противном случае, если объединяется более одного входного dtype, выход будет в собственном порядке байтов.

После выделения с этим флагом вызывающая сторона может получить новый массив, вызвав NpyIter_GetOperandArray и получение i-го объекта в возвращённом C-массиве. Вызывающая сторона должна вызвать Py_INCREF на нём, чтобы получить ссылку на массив.

NPY_ITER_NO_SUBTYPE#

Для использования с NPY_ITER_ALLOCATE, этот флаг отключает выделение подтипа массива для вывода, заставляя его быть простым ndarray.

TODO: Возможно, было бы лучше ввести функцию NpyIter_GetWrappedOutput и удалить этот флаг?

NPY_ITER_NO_BROADCAST#

Гарантирует, что входные или выходные данные точно соответствуют размерностям итерации.

NPY_ITER_ARRAYMASK#

Указывает, что этот операнд является маской для выбора элементов при записи в операнды, которые имеют NPY_ITER_WRITEMASKED флаг применён к ним. Только один операнд может иметь NPY_ITER_ARRAYMASK флаг применён к нему.

Тип данных операнда с этим флагом должен быть либо NPY_BOOL, NPY_MASK, или структурированный тип данных, поля которого все являются допустимыми типами данных для масок. В последнем случае он должен соответствовать структурированному операнду с WRITEMASKED, так как он указывает маску для каждого поля этого массива.

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

NPY_ITER_WRITEMASKED#

Этот массив является маской для всех 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 флаги, NPY_ORDER порядок, NPY_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, когда не равен нулю или -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-D массиву, 2-D массиву, 1-D массиву и скаляру.

Примечание: До NumPy 1.8 oa_ndim == 0 использовался для сигнализации о том, что 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};

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

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

int NpyIter_RemoveAxis(NpyIter *iter, int ось)#

Удаляет ось из итерации. Это требует, чтобы 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.

NPY_ARRAYMETHOD_FLAGS NpyIter_GetTransferFlags(NpyIter *iter)#

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

Извлекает NPY_METH_RUNTIME_FLAGS которые предоставляют информацию о требуется ли буферизации Python GIL (NPY_METH_REQUIRES_PYAPI) или могут быть установлены ошибки с плавающей запятой (NPY_METH_NO_FLOATINGPOINT_ERRORS).

До NumPy 2.3 доступной публичной функцией была NpyIter_IterationNeedsAPI, который всё ещё доступен и дополнительно проверяет объектные (или подобные) типы данных, а не исключительно потребности буферизации/итерации. В целом, этой функции следует отдавать предпочтение.

int NpyIter_Reset(NpyIter *iter, char **errmsg)#

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

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

int NpyIter_ResetToIterIndexRange(NpyIter *iter, npy_intp istart, npy_intp iend, char **errmsg)#

Сбрасывает итератор и ограничивает его iterindex range [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, функцию можно безопасно вызывать без удержания GIL Python.

int NpyIter_ResetBasePointers(NpyIter *iter, char **baseptrs, char **errmsg)#

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

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

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)#

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

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

int NpyIter_GotoIndex(NpyIter *iter, npy_intp index)#

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

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

npy_intp NpyIter_GetIterSize(NpyIter *iter)#

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

npy_intp NpyIter_GetIterIndex(NpyIter *iter)#

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

void NpyIter_GetIterIndexRange(NpyIter *iter, npy_intp *istart, npy_intp *iend)#

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

int NpyIter_GotoIterIndex(NpyIter *iter, npy_intp iterindex)#

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

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

npy_bool NpyIter_HasDelayedBufAlloc(NpyIter *iter)#

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

npy_bool NpyIter_HasExternalLoop(NpyIter *iter)#

Возвращает 1, если вызывающей стороне нужно обработать самый внутренний одномерный цикл, или 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 ось)#

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

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

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

int NpyIter_GetShape(NpyIter *iter, npy_intp *выходная форма)#

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

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

PyArray_Descr **NpyIter_GetDescrArray(NpyIter *iter)#

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

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

PyObject **NpyIter_GetOperandArray(NpyIter *iter)#

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

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 флаг, где NULL был передан для op_axes. Это для данных, упакованных непрерывно, но не обязательно в порядке 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)#

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

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

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 не изменит его. Эту функцию можно безопасно вызывать без удержания GIL Python.

ПРЕДУПРЕЖДЕНИЕ: Хотя указатель может быть кэширован, его значения могут изменяться, если итератор буферизован.

npy_intp *NpyIter_GetInnerLoopSizePtr(NpyIter *iter)#

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

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

void NpyIter_GetInnerFixedStrideArray(NpyIter *iter, npy_intp *out_strides)#

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

После того как итератор подготовлен для итерации (после сброса, если NPY_ITER_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