Spec-Zone.ru › NumPy 2.0

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;
}

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

Этот пример показывает, как работать со флагом 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_uint32flags, NPY_ORDERorder, NPY_CASTINGcasting, PyArray_Descr*dtype)

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

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

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

Любое из значений перечисления NPY_CASTING может быть передано casting. Значения включают NPY_NO_CASTING, NPY_EQUIV_CASTING, NPY_SAFE_CASTING, NPY_SAME_KIND_CASTING и NPY_UNSAFE_CASTING. Для выполнения преобразований также необходимо включить копирование или буферизацию.

Если dtype не NULL, то требуется указанный тип данных. Если разрешено копирование, то при возможности преобразования данных будет создана временная копия. Если включен NPY_ITER_UPDATEIFCOPY, то при уничтожении итератора данные также будут скопированы обратно с другим преобразованием.

Возвращает NULL, если возникла ошибка, иначе возвращает выделенный итератор.

Для создания итератора, аналогичного старому итератору, это должно работать.

iter = NpyIter_New(op, NPY_ITER_READWRITE,
                    NPY_CORDER, NPY_NO_CASTING, NULL);

Если нужно изменить массив с выровненным double кодом, но порядок не важен, то используется это.

dtype = PyArray_DescrFromType(NPY_DOUBLE);
iter = NpyIter_New(op, NPY_ITER_READWRITE|
                    NPY_ITER_BUFFERED|
                    NPY_ITER_NBO|
                    NPY_ITER_ALIGNED,
                    NPY_KEEPORDER,
                    NPY_SAME_KIND_CASTING,
                    dtype);
Py_DECREF(dtype);
NpyIter*NpyIter_MultiNew(npy_intpnop, PyArrayObject**op, npy_uint32flags, NPY_ORDERorder, NPY_CASTINGcasting, npy_uint32*op_flags, PyArray_Descr**op_dtypes)

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

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

Любое из значений перечисления NPY_CASTING может быть передано casting. Значения включают NPY_NO_CASTING, NPY_EQUIV_CASTING, NPY_SAFE_CASTING, NPY_SAME_KIND_CASTING и NPY_UNSAFE_CASTING. Для выполнения преобразований также необходимо включить копирование или буферизацию.

Если op_dtypes не NULL, то это указывает на тип данных или NULL для каждого op[i].

Возвращает NULL, если возникла ошибка, иначе возвращает выделенный итератор.

Флаги, которые могут быть переданы flags, относящиеся ко всему итератору, это:

NPY_ITER_C_INDEX

Приводит к отслеживанию итератором плоского индекса, соответствующего порядку C. Этот вариант нельзя использовать с NPY_ITER_F_INDEX.

NPY_ITER_F_INDEX

Приводит к отслеживанию итератором плоского индекса, соответствующего порядку Fortran. Этот вариант нельзя использовать с NPY_ITER_C_INDEX.

NPY_ITER_MULTI_INDEX

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

NPY_ITER_EXTERNAL_LOOP

Приводит к пропуску итерации внутреннего цикла, требуя от пользователя итератора обработки его.

Этот флаг несовместим с NPY_ITER_C_INDEX, NPY_ITER_F_INDEX и NPY_ITER_MULTI_INDEX.

NPY_ITER_DONT_NEGATE_STRIDES

Это влияет только на итератор, когда для параметра порядка указано NPY_KEEPORDER. По умолчанию с NPY_KEEPORDER итератор меняет оси с отрицательными шагами, чтобы память проходилась вперёд. Это отключает эту операцию. Используйте этот флаг, если нужно использовать внутренний порядок памяти осей, но не нужно изменять направление обхода по оси. Такое поведение, например, numpy.ravel(a, order='K').

NPY_ITER_COMMON_DTYPE

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

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

NPY_ITER_REFS_OK

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

NPY_ITER_ZEROSIZE_OK

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

NPY_ITER_REDUCE_OK

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

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

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

NPY_ITER_RANGED

Включает поддержку итерации по поддиапазонам полного iterindex диапазона [0, NpyIter_IterSize(iter)). Используйте функцию NpyIter_ResetToIterIndexRange для указания диапазона для итерации.

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

NPY_ITER_BUFFERED

Заставляет итератор хранить данные буферизации и использовать буферизацию для удовлетворения требований к типу данных, выравниванию и порядку байтов. Чтобы буферизовать операнд, не указывайте флаги NPY_ITER_COPY или NPY_ITER_UPDATEIFCOPY, поскольку они переопределяют буферизацию. Буферизация особенно полезна для кода Python, использующего итератор, позволяя обрабатывать большие блоки данных сразу, чтобы амортизировать издержки интерпретатора Python.

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

Обратите внимание, что если операнду задан флаг NPY_ITER_COPY или NPY_ITER_UPDATEIFCOPY, будет произведена копия вместо буферизации. Буферизация всё ещё произойдёт, когда массив был расширен, поэтому элементы необходимо дублировать, чтобы получить постоянный шаг.

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

NPY_ITER_GROWINNER

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

NPY_ITER_DELAY_BUFALLOC

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

Другое использование этого флага — для настройки операций сокращения. После создания итератора и автоматического выделения выхода сокращения итератором (убедитесь, что используется доступ READWRITE), его значение может быть инициализировано единицей сокращения. Используйте NpyIter_GetOperandArray для получения объекта. Затем вызовите NpyIter_Reset для выделения и заполнения буферов их начальными значениями.

NPY_ITER_COPY_IF_OVERLAP

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

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

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

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

NPY_ITER_READWRITE
NPY_ITER_READONLY
NPY_ITER_WRITEONLY

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

NPY_ITER_COPY

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

NPY_ITER_UPDATEIFCOPY

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

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

NPY_ITER_NBO
NPY_ITER_ALIGNED
NPY_ITER_CONTIG

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

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

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

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

NPY_ITER_ALLOCATE

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

Когда op[i] равно NULL, запрашиваемый тип данных op_dtypes[i] также может быть NULL, в этом случае он автоматически генерируется из типов данных массивов, помеченных как читаемые. Правила генерации типа данных такие же, как для 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_intpnop, PyArrayObject**op, npy_uint32flags, NPY_ORDERorder, NPY_CASTINGcasting, npy_uint32*op_flags, PyArray_Descr**op_dtypes, intoa_ndim, int**op_axes, npy_intpconst*itershape, npy_intpbuffersize)

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

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

Параметр oa_ndim, когда не равен нулю или -1, определяет количество измерений, которые будут проитерированы с настраиваемым вещанием. Если он предоставлен, op_axes должен, а itershape может быть предоставлен. Параметр op_axes позволяет подробно контролировать, как оси массивов операндов сопоставляются и проитерированы. В op_axes, вы должны предоставить массив указателей на массивы размером nop типа 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 ``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 равно нулю, используется размер буфера по умолчанию, в противном случае он определяет размер буфера для использования. Рекомендуется использовать буферы, которые являются степенями двойки, такие как 4096 или 8192.

Возвращает 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 должен быть вызван для каждой копии.

intNpyIter_RemoveAxis(NpyIter*iter, intaxis)

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

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

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

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

intNpyIter_RemoveMultiIndex(NpyIter*iter)

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

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

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

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

intNpyIter_EnableExternalLoop(NpyIter*iter)

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

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

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

intNpyIter_Deallocate(NpyIter*iter)

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

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

intNpyIter_Reset(NpyIter*iter, char**errmsg)

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

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

intNpyIter_ResetToIterIndexRange(NpyIter*iter, npy_intpistart, npy_intpiend, char**errmsg)

Сбрасывает итератор и ограничивает его диапазоном iterindex [istart, iend). См. NpyIter_Copy для объяснения использования этого для многопоточной итерации. Для этого требуется, чтобы флаг NPY_ITER_RANGED был передан при создании итератора.

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

/* Set to a trivial empty range */
NpyIter_ResetToIterIndexRange(iter, 0, 0);
/* Set the base pointers */
NpyIter_ResetBasePointers(iter, baseptrs);
/* Set to the desired range */
NpyIter_ResetToIterIndexRange(iter, istart, iend);

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

intNpyIter_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));
intNpyIter_GotoMultiIndex(NpyIter*iter, npy_intpconst*multi_index)

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

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

intNpyIter_GotoIndex(NpyIter*iter, npy_intpindex)

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

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

npy_intpNpyIter_GetIterSize(NpyIter*iter)

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

npy_intpNpyIter_GetIterIndex(NpyIter*iter)

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

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

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

intNpyIter_GotoIterIndex(NpyIter*iter, npy_intpiterindex)

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

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

npy_boolNpyIter_HasDelayedBufAlloc(NpyIter*iter)

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

npy_boolNpyIter_HasExternalLoop(NpyIter*iter)

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

npy_boolNpyIter_HasMultiIndex(NpyIter*iter)

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

npy_boolNpyIter_HasIndex(NpyIter*iter)

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

npy_boolNpyIter_RequiresBuffering(NpyIter*iter)

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

npy_boolNpyIter_IsBuffered(NpyIter*iter)

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

npy_boolNpyIter_IsGrowInner(NpyIter*iter)

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

npy_intpNpyIter_GetBufferSize(NpyIter*iter)

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

intNpyIter_GetNDim(NpyIter*iter)

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

intNpyIter_GetNOp(NpyIter*iter)

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

npy_intp*NpyIter_GetAxisStrideArray(NpyIter*iter, intaxis)

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

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

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

intNpyIter_GetShape(NpyIter*iter, npy_intp*outshape)

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

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

PyArray_Descr**NpyIter_GetDescrArray(NpyIter*iter)

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

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

PyObject**NpyIter_GetOperandArray(NpyIter*iter)

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

PyObject*NpyIter_GetIterView(NpyIter*iter, npy_intpi)

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

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

voidNpyIter_GetReadFlags(NpyIter*iter, char*outreadflags)

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

voidNpyIter_GetWriteFlags(NpyIter*iter, char*outwriteflags)

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

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

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

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

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

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

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

npy_boolNpyIter_IsFirstVisit(NpyIter*iter, intiop)

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

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

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

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

ПРЕДУПРЕЖДЕНИЕ: По соображениям производительности, ‘iop’ не проверяется на границы, не подтверждается, что ‘iop’ фактически является операндом сокращения, и не подтверждается, что режим EXTERNAL_LOOP включен. Эти проверки являются обязанностью вызывающей стороны и должны выполняться вне любых внутренних циклов.

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

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

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

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

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

NpyIter_IterNextFunc *iternext = NpyIter_GetIterNext(iter, NULL);
char** dataptr = NpyIter_GetDataPtrArray(iter);

do {
    /* use the addresses dataptr[0], ... dataptr[nop-1] */
} while(iternext(iter));

Когда указан NPY_ITER_EXTERNAL_LOOP, типичная внутренняя конструкция цикла выглядит так.

NpyIter_IterNextFunc *iternext = NpyIter_GetIterNext(iter, NULL);
char** dataptr = NpyIter_GetDataPtrArray(iter);
npy_intp* stride = NpyIter_GetInnerStrideArray(iter);
npy_intp* size_ptr = NpyIter_GetInnerLoopSizePtr(iter), size;
npy_intp iop, nop = NpyIter_GetNOp(iter);

do {
    size = *size_ptr;
    while (size--) {
        /* use the addresses dataptr[0], ... dataptr[nop-1] */
        for (iop = 0; iop < nop; ++iop) {
            dataptr[iop] += stride[iop];
        }
    }
} while (iternext());

Обратите внимание, что мы используем массив dataptr внутри итератора, а не копируем значения во временную локальную переменную. Это возможно, потому что, когда iternext() вызывается, эти указатели будут перезаписаны свежими значениями, а не инкрементально обновлены.

Если используется буфер фиксированного размера во время компиляции (оба флага NPY_ITER_BUFFERED и NPY_ITER_EXTERNAL_LOOP), внутренний размер также может использоваться как сигнал. Размер гарантированно станет нулевым, когда iternext() возвращает false, что позволяет использовать следующую конструкцию цикла. Обратите внимание, что если вы используете эту конструкцию, не следует передавать NPY_ITER_GROWINNER в качестве флага, так как это приведёт к увеличению размера в некоторых случаях.

/* The constructor should have buffersize passed as this value */
#define FIXED_BUFFER_SIZE 1024

NpyIter_IterNextFunc *iternext = NpyIter_GetIterNext(iter, NULL);
char **dataptr = NpyIter_GetDataPtrArray(iter);
npy_intp *stride = NpyIter_GetInnerStrideArray(iter);
npy_intp *size_ptr = NpyIter_GetInnerLoopSizePtr(iter), size;
npy_intp i, iop, nop = NpyIter_GetNOp(iter);

/* One loop with a fixed inner size */
size = *size_ptr;
while (size == FIXED_BUFFER_SIZE) {
    /*
     * This loop could be manually unrolled by a factor
     * which divides into FIXED_BUFFER_SIZE
     */
    for (i = 0; i < FIXED_BUFFER_SIZE; ++i) {
        /* use the addresses dataptr[0], ... dataptr[nop-1] */
        for (iop = 0; iop < nop; ++iop) {
            dataptr[iop] += stride[iop];
        }
    }
    iternext();
    size = *size_ptr;
}

/* Finish-up loop with variable inner size */
if (size > 0) do {
    size = *size_ptr;
    while (size--) {
        /* use the addresses dataptr[0], ... dataptr[nop-1] */
        for (iop = 0; iop < nop; ++iop) {
            dataptr[iop] += stride[iop];
        }
    }
} while (iternext());
NpyIter_GetMultiIndexFunc*NpyIter_GetGetMultiIndex(NpyIter*iter, char**errmsg)

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

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

char**NpyIter_GetDataPtrArray(NpyIter*iter)

Возвращает указатель на массив указателей данных. Если 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)

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

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

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

npy_intp*NpyIter_GetInnerLoopSizePtr(NpyIter*iter)

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

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

voidNpyIter_GetInnerFixedStrideArray(NpyIter*iter, npy_intp*out_strides)

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

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

Функция может быть безопасно вызвана без блокировки 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–2024 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/2.0/reference/c-api/iterator.html

Spec-Zone.ru

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