Python类型和C结构#

在C代码中定义了几种新类型.其中大多数可以从Python访问,但由于其有限的用途,一些没有公开.每个新的Python类型都有一个关联的 PyObject* ,其内部结构包含一个指向“方法表”的指针,该表定义了新对象在Python中的行为方式.当您在C代码中接收到Python对象时,您总是会获得一个指向 PyObject 结构的指针.因为 PyObject 结构非常通用,并且只定义了 PyObject_HEAD ,所以它本身并不是很有趣.但是,不同的对象在 PyObject_HEAD 之后包含更多细节(但您必须强制转换为正确的类型才能访问它们—或者使用访问器函数或宏).

定义的新Python类型#

Python类型在C中与Python中的类在功能上等效.通过构造一个新的Python类型,您可以为Python提供一个新对象.ndarray对象是在C中定义的新类型的一个例子.通过两个基本步骤在C中定义新类型:

  1. 创建一个C结构体(通常命名为 Py{Name}Object ),该结构体与 PyObject 结构体本身在二进制上兼容,但包含该特定对象所需的额外信息;

  2. 使用指向函数的指针填充 PyTypeObject 表(由 PyObject 结构的 ob_type 成员指向),这些函数实现该类型所需的行为.

与定义 Python 类行为的特殊方法名不同,这里有“函数表”,其中包含指向实现所需结果的函数的指针.PyTypeObject 本身是动态的,这允许 C 类型从 C 中的其他 C 类型“子类型化”,并在 Python 中进行子类化.子类型继承其父类型的属性和方法.

有两个主要的新类型:ndarray ( PyArray_Type ) 和 ufunc ( PyUFunc_Type ).其他类型起辅助作用: PyArrayIter_Type , PyArrayMultiIter_TypePyArrayDescr_Type . PyArrayIter_Type 是 ndarray 的扁平迭代器的类型(获取 flat 属性时返回的对象). PyArrayMultiIter_Type 是调用 broadcast 时返回的对象的类型.它处理嵌套序列集合的迭代和广播.此外, PyArrayDescr_Type 是数据类型描述符类型,其实例描述数据,而 PyArray_DTypeMeta 是数据类型描述符的元类.还有新的标量数组类型,它们是与可用于数组的每个基本数据类型相对应的新 Python 标量.其他类型是占位符,允许数组标量适合实际 Python 类型的层次结构.最后,与 NumPy 内置数据类型相对应的 PyArray_DTypeMeta 实例也是公开可见的.

PyArray_Type 和 PyArrayObject#

PyTypeObject PyArray_Type#

ndarray 的 Python 类型是 PyArray_Type .在 C 中,每个 ndarray 都是指向 PyArrayObject 结构的指针.此结构的 ob_type 成员包含指向 PyArray_Type 类型对象的指针.

type PyArrayObject#
type NPY_AO#

PyArrayObject C 结构体包含数组的所有必需信息.ndarray(及其子类)的所有实例都将具有此结构.为了将来的兼容性,通常应使用提供的宏访问这些结构成员.如果需要更短的名称,则可以使用定义为等效于 NPY_AOPyArrayObject (已弃用).直接访问结构字段已被弃用.请改用 PyArray_*(arr) 形式.从 NumPy 1.20 开始,此结构的大小不被视为 NumPy ABI 的一部分(请参阅成员列表末尾的注释).

typedef struct PyArrayObject {
    PyObject_HEAD
    char *data;
    int nd;
    npy_intp *dimensions;
    npy_intp *strides;
    PyObject *base;
    PyArray_Descr *descr;
    int flags;
    PyObject *weakreflist;
    /* version dependent private members */
} PyArrayObject;
PyObject_HEAD

所有 Python 对象都需要此项.它由(至少)一个引用计数成员 ( ob_refcnt ) 和一个指向类型对象 ( ob_type ) 的指针组成.(如果使用特殊选项编译 Python,则可能还存在其他元素,有关更多信息,请参见 Python 源代码树中的 Include/object.h).ob_type 成员指向一个 Python 类型对象.

char *data#

可以通过 PyArray_DATA 访问,此数据成员是指向数组第一个元素的指针.此指针可以(并且通常应该)被强制转换为数组的数据类型.

int nd#

一个整数,提供此数组的维数.当 nd 为 0 时,数组有时称为 rank-0 数组.此类数组具有未定义的维数和步幅,无法访问. ndarraytypes.h 中定义的宏 PyArray_NDIM 指向此数据成员. NPY_MAXDIMS 被定义为编译时常量,限制维数的数量.自 NumPy 2 以来,此数字为 64,之前为 32.但是,我们可能希望将来删除此限制,因此最好显式检查依赖于此类上限的代码的维度.

npy_intp *dimensions#

一个整数数组,只要 nd \(\geq\) 1,就提供每个维度的形状.该整数总是足够大,可以在平台上保存一个指针,因此维度大小仅受内存限制. PyArray_DIMS 是与此数据成员关联的宏.

npy_intp *strides#

一个整数数组,提供每个维度中必须跳过的字节数才能到达该维度中的下一个元素.与宏 PyArray_STRIDES 关联.

PyObject *base#

此成员由 PyArray_BASE 指向,用于保存指向与此数组相关的另一个 Python 对象的指针.有两种用例:

  • 如果此数组不拥有自己的内存,则 base 指向拥有它的 Python 对象(可能是另一个数组对象).

  • 如果此数组设置了 NPY_ARRAY_WRITEBACKIFCOPY 标志,则此数组是“行为不端”数组的工作副本.

当调用 PyArray_ResolveWritebackIfCopy 时,base 指向的数组将使用此数组的内容进行更新.

PyArray_Descr *descr#

指向数据类型描述符对象(见下文)的指针.数据类型描述符对象是新内置类型的实例,允许对内存进行通用描述.每个支持的数据类型都有一个描述符结构.此描述符结构包含有关该类型的有用信息,以及指向函数指针表的指针,以实现特定功能.顾名思义,它与宏 PyArray_DESCR 相关联.

int flags#

此数据成员由宏 PyArray_FLAGS 指向,表示指示如何解释 data 指向的内存的标志.可能的标志是 NPY_ARRAY_C_CONTIGUOUS , NPY_ARRAY_F_CONTIGUOUS , NPY_ARRAY_OWNDATA , NPY_ARRAY_ALIGNED , NPY_ARRAY_WRITEABLE , NPY_ARRAY_WRITEBACKIFCOPY .

PyObject *weakreflist#

此成员允许数组对象具有弱引用(使用 weakref 模块).

备注

进一步的成员被认为是私有的并且依赖于版本.如果 struct 的大小对您的代码很重要,则必须特别小心.一个可能的使用场景是 C 中的子类化.如果您的代码依赖于 sizeof(PyArrayObject) 是常量,您必须在导入时添加以下检查:

if (sizeof(PyArrayObject) < PyArray_Type.tp_basicsize) {
    PyErr_SetString(PyExc_ImportError,
       "Binary incompatibility with NumPy, must recompile/update X.");
    return NULL;
}

为确保您的代码不必针对特定的 NumPy 版本进行编译,您可以添加一个常量,为 NumPy 中的更改留出空间.保证与任何未来 NumPy 版本兼容的解决方案需要使用运行时计算偏移量和分配大小.

PyArray_Type typeobject 实现了 Python objects 的许多功能,包括 tp_as_number , tp_as_sequence , tp_as_mappingtp_as_buffer 接口. rich comparison ) 也与成员 ( tp_members ) 和属性 ( tp_getset ) 的新式属性查找一起使用. PyArray_Type 也可以是子类型的.

小技巧

tp_as_number 方法使用通用方法来调用为处理该操作而注册的任何函数.导入 _multiarray_umath 模块时,它会将所有数组的数值运算设置为相应的 ufunc.可以使用 PyUFunc_ReplaceLoopBySignature 更改此选择.

PyGenericArrType_Type#

PyTypeObject PyGenericArrType_Type#

PyGenericArrType_Type 是创建 numpy.generic python 类型的 PyTypeObject 定义.

PyArrayDescr_Type and PyArray_Descr#

PyTypeObject PyArrayDescr_Type#

PyArrayDescr_Type 是数据类型描述符对象的内置类型,用于描述如何解释构成数组的字节.有 21 个静态定义的 PyArray_Descr 对象用于内置数据类型.虽然这些对象参与引用计数,但它们的引用计数永远不应达到零.还有一个用户定义的 PyArray_Descr 对象的动态表,也需要维护.一旦数据类型描述符对象被“注册”,它也绝不应被释放.函数 PyArray_DescrFromType (…) 可以用于从枚举类型编号(内置或用户定义)中检索 PyArray_Descr 对象.

type PyArray_DescrProto#

PyArray_Descr 结构相同.此结构用于静态定义原型,以便通过 PyArray_RegisterDataType 注册新的遗留 DType.

有关详细信息,请参见 PyArray_RegisterDataType 中的注释.

type PyArray_Descr#

PyArray_Descr 结构是 PyArrayDescr_Type 的核心.虽然为了完整起见在此处进行了描述,但应将其视为 NumPy 的内部结构,并通过 PyArrayDescr_*PyDataType* 函数和宏进行操作.此结构的大小可能会因 NumPy 版本而异.为了确保兼容性:

  • 永远不要声明该结构的非指针实例

  • 永远不要执行指针运算

  • 永远不要使用 sizeof(PyArray_Descr)

它具有以下结构:

typedef struct {
    PyObject_HEAD
    PyTypeObject *typeobj;
    char kind;
    char type;
    char byteorder;
    char _former_flags;  // unused field
    int type_num;
    /*
     * Definitions after this one must be accessed through accessor
     * functions (see below) when compiling with NumPy 1.x support.
     */
    npy_uint64 flags;
    npy_intp elsize;
    npy_intp alignment;
    NpyAuxData *c_metadata;
    npy_hash_t hash;
    void *reserved_null[2];  // unused field, must be NULLed.
} PyArray_Descr;

一些 dtype 具有可通过 PyDataType_NAMES , PyDataType_FIELDS , PyDataType_SUBARRAY 访问的其他成员,在某些情况下(时间)可以通过 PyDataType_C_METADATA 访问.

PyTypeObject *typeobj#

指向类型对象的指针,该类型对象是此数组元素对应的 Python 类型.对于内置类型,这会指向相应的数组标量.对于用户定义的类型,这应指向用户定义的类型对象.此类型对象可以继承自数组标量,也可以不继承.如果不继承自数组标量,则应在 flags 成员中设置 NPY_USE_GETITEMNPY_USE_SETITEM 标志.

char kind#

一个字符代码,指示数组的种类(使用数组接口类型字符串表示法).“b”表示布尔值,“i”表示有符号整数,“u”表示无符号整数,“f”表示浮点数,“c”表示复数浮点数,“S”表示 8 位以零结尾的字节,“U”表示 32 位/字符 Unicode 字符串,“V”表示任意.

char type#

指示数据类型的传统字符代码.

char byteorder#

一个字符,指示字节顺序:“>”(大端序),“<”(小端序),“=”(本机),“|”(不相关,忽略).所有内置数据类型都具有字节顺序“=”.

npy_uint64 flags#

一种数据类型位标志,用于确定数据类型是否表现出类似对象数组的行为.此成员中的每一位都是一个标志,命名如下:

int type_num#

唯一标识数据类型的数字.对于新的数据类型,此数字在注册数据类型时分配.

npy_intp elsize#

对于大小始终相同的数据类型(例如 long),这会保存数据类型的大小.对于灵活的数据类型,其中不同的数组可以具有不同的元素大小,这应为 0.

请参阅 PyDataType_ELSIZEPyDataType_SET_ELSIZE ,以获得一种与 NumPy 1.x 兼容的方式来访问此字段.

npy_intp alignment#

一个提供此数据类型对齐信息的数字.具体来说,它显示了编译器将此类型的项目放置在距2元素结构(其第一个元素是 char )开头多远的位置: offsetof(struct {char c; type v;}, v)

请参阅 PyDataType_ALIGNMENT ,以获得一种与 NumPy 1.x 兼容的方式来访问此字段.

PyObject *metadata#

有关此 dtype 的元数据.

NpyAuxData *c_metadata#

特定于特定 dtype 的 C 实现的元数据.为 NumPy 1.7.0 添加.

type npy_hash_t#
npy_hash_t *hash#

用于缓存哈希值.

NPY_ITEM_REFCOUNT#

表示此数据类型的项目必须进行引用计数(使用 Py_INCREFPy_DECREF ).

NPY_ITEM_HASOBJECT#

NPY_ITEM_REFCOUNT 相同.

NPY_LIST_PICKLE#

指示此数据类型的数组在 pickle 之前必须转换为列表.

NPY_ITEM_IS_POINTER#

指示该项是指向其他数据类型的指针

NPY_NEEDS_INIT#

指示必须在创建时初始化(设置为 0)此数据类型的内存.

NPY_NEEDS_PYAPI#

指示此数据类型在访问期间需要 Python C-API(因此,如果需要数组访问,请不要放弃 GIL).

NPY_USE_GETITEM#

在数组访问时,使用 f->getitem 函数指针,而不是标准转换为数组标量.如果您没有定义一个与数据类型一起使用的数组标量,则必须使用.

NPY_USE_SETITEM#

从数组标量创建 0-d 数组时,使用 f->setitem 而不是从数组标量的标准复制.如果您没有定义一个与数据类型一起使用的数组标量,则必须使用.

NPY_FROM_FIELDS#

如果在数据类型的任何字段中设置了这些位,则为父数据类型继承的位.当前 ( NPY_NEEDS_INIT | NPY_LIST_PICKLE | NPY_ITEM_REFCOUNT | NPY_NEEDS_PYAPI ).

NPY_OBJECT_DTYPE_FLAGS#

为对象数据类型设置的位:( NPY_LIST_PICKLE | NPY_USE_GETITEM | NPY_ITEM_IS_POINTER | NPY_ITEM_REFCOUNT | NPY_NEEDS_INIT | NPY_NEEDS_PYAPI ).

int PyDataType_FLAGCHK(PyArray_Descr *dtype, int flags)#

如果为数据类型对象设置了所有给定标志,则返回 true.

int PyDataType_REFCHK(PyArray_Descr *dtype)#

等效于 PyDataType_FLAGCHK (dtype, NPY_ITEM_REFCOUNT ).

PyArray_ArrFuncs#

PyArray_ArrFuncs *PyDataType_GetArrFuncs(PyArray_Descr *dtype)#

获取数据类型的旧版 PyArray_ArrFuncs (不会失败).

在 NumPy 版本加入: 2.0 此函数已在 NumPy 2.0 中以向后兼容和可移植的方式添加(请参阅 npy_2_compat.h ).任何以前访问 PyArray_Descr->f 插槽的代码,现在必须使用此函数并将其向后移植以使用 1.x 编译.( npy_2_compat.h 标头可以为此目的进行供应商化.)

type PyArray_ArrFuncs#

实现内部功能的函数.并非所有这些函数指针都必须为给定的类型定义.所需的成员为 nonzero , copyswap , copyswapn , setitem , getitemcast .这些函数指针被假定为非 NULL ,而 NULL 条目将导致程序崩溃.其他函数可能为 NULL ,这仅意味着该数据类型的功能减少.(此外,如果您在注册用户定义的数据类型时 nonzero 函数为 NULL ,则将使用默认函数填充它).

typedef struct {
    PyArray_VectorUnaryFunc *cast[NPY_NTYPES_LEGACY];
    PyArray_GetItemFunc *getitem;
    PyArray_SetItemFunc *setitem;
    PyArray_CopySwapNFunc *copyswapn;
    PyArray_CopySwapFunc *copyswap;
    PyArray_CompareFunc *compare;
    PyArray_ArgFunc *argmax;
    PyArray_DotFunc *dotfunc;
    PyArray_ScanFunc *scanfunc;
    PyArray_FromStrFunc *fromstr;
    PyArray_NonzeroFunc *nonzero;
    PyArray_FillFunc *fill;
    PyArray_FillWithScalarFunc *fillwithscalar;
    PyArray_SortFunc *sort[NPY_NSORTS];
    PyArray_ArgSortFunc *argsort[NPY_NSORTS];
    PyObject *castdict;
    PyArray_ScalarKindFunc *scalarkind;
    int **cancastscalarkindto;
    int *cancastto;
    void *_unused1;
    void *_unused2;
    void *_unused3;
    PyArray_ArgFunc *argmin;
} PyArray_ArrFuncs;

函数指针的描述中使用了行为良好的段的概念.行为良好的段是针对数据类型对齐且采用本机机器字节顺序的段. nonzero , copyswap , copyswapn , getitemsetitem 函数可以(并且必须)处理行为不端的数组.其他函数需要行为良好的内存段.

备注

这些函数主要是旧版 API,但是,有些函数仍然在使用.从 NumPy 2.x 开始,它们只能通过 PyDataType_GetArrFuncs 访问(有关更多详细信息,请参见该函数).在使用结构中定义的任何函数之前,应检查它是否为 NULL .通常,可以预期定义了函数 getitem , setitem , copyswapcopyswapn ,但是预计所有函数都将被更新的 API 替换.例如, PyArray_Packsetitem 的更强大版本,例如,它可以正确处理强制类型转换.

void cast(void *from, void *to, npy_intp n, void *fromarr, void *toarr)#

一个函数指针数组,用于将当前类型转换为所有其他内置类型.每个函数将 from 指向的连续,对齐且未交换的缓冲区转换为 to 指向的连续,对齐且未交换的缓冲区.要转换的条目数由 n 给出,参数 fromarrtoarr 被解释为 PyArrayObjects ,用于灵活数组以获取项目大小信息.

PyObject *getitem(void *data, void *arr)#

指向一个函数的指针,该函数从数组对象 arrdata 指向的单个元素返回一个标准的 Python 对象.此函数必须能够正确处理“行为不端”(未对齐和/或已交换)的数组.

int setitem(PyObject *item, void *data, void *arr)#

指向一个函数的指针,该函数将 Python 对象 item 设置到数组 arr 中,位于 data 指向的位置.此函数处理“行为不端”的数组.如果成功,则返回零,否则返回负一(并设置 Python 错误).

void copyswapn(void *dest, npy_intp dstride, void *src, npy_intp sstride, npy_intp n, int swap, void *arr)#
void copyswap(void *dest, void *src, int swap, void *arr)#

这些成员都是指向函数的指针,用于将数据从 src 复制到 dest 并在指示时交换. arr 的值仅用于灵活的( NPY_STRING , NPY_UNICODENPY_VOID )数组(并从 arr->descr->elsize 获取).第二个函数复制单个值,而第一个函数使用提供的步幅循环遍历 n 个值.这些函数可以处理行为不端的 src 数据.如果 src 为 NULL,则不执行复制.如果 swap 为 0,则不进行字节交换.假设 destsrc 不重叠.如果它们重叠,则首先使用 memmove (…),然后使用 NULL 值的 src 进行 copyswap(n) .

int compare(const void *d1, const void *d2, void *arr)#

指向一个函数的指针,该函数比较数组 arr 的两个元素,由 d1d2 指向.此函数需要行为良好(对齐且未交换)的数组.如果 * d1 > * d2 ,则返回值为 1;如果 * d1 == * d2 ,则返回值为 0;如果 * d1 < * d2 ,则返回值为 -1.数组对象 arr 用于检索灵活数组的项目大小和字段信息.

int argmax(void *data, npy_intp n, npy_intp *max_ind, void *arr)#

指向一个函数的指针,该函数检索从由 data 指向的元素开始的 arrn 个元素中最大的元素的索引.此函数要求内存段是连续且行为良好的.返回值始终为 0.最大元素的索引在 max_ind 中返回.

void dotfunc(void *ip1, npy_intp is1, void *ip2, npy_intp is2, void *op, npy_intp n, void *arr)#

指向一个函数的指针,该函数将两个长度为 n 的序列相乘,将它们相加,然后将结果放入 arrop 指向的元素中.两个序列的起始位置分别由 ip1ip2 指向.要到达每个序列中的下一个元素,分别需要跳跃 is1is2 字节.此函数需要行为良好(尽管不一定是连续的)的内存.

int scanfunc(FILE *fd, void *ip, void *arr)#

指向一个函数的指针,该函数从文件描述符 fd 中扫描(scanf 样式)相应类型的一个元素到由 ip 指向的数组内存中.假设该数组的行为良好.最后一个参数 arr 是要扫描到的数组.返回成功分配的接收参数的数量(如果在分配第一个接收参数之前发生匹配失败,则该数量可能为零),或者如果在分配第一个接收参数之前发生输入失败,则返回 EOF.应该在不持有 Python GIL 的情况下调用此函数,并且必须获取它以进行错误报告.

int fromstr(char *str, void *ip, char **endptr, void *arr)#

指向一个函数的指针,该函数将 str 指向的字符串转换为相应类型的一个元素,并将其放置在 ip 指向的内存位置.转换完成后, *endptr 指向字符串的其余部分.最后一个参数 arrip 指向的数组(可变大小数据类型需要).成功时返回 0,失败时返回 -1.需要行为良好的数组.应该在不持有 Python GIL 的情况下调用此函数,并且必须获取它以进行错误报告.

npy_bool nonzero(void *data, void *arr)#

指向一个函数的指针,如果 data 指向的 arr 的项目非零,则返回 TRUE.此函数可以处理行为不端的数组.

void fill(void *data, npy_intp length, void *arr)#

指向一个函数的指针,该函数用数据填充给定长度的连续数组.数组的前两个元素必须已填充.将从这两个值计算出一个增量,并通过重复添加此计算出的增量来计算从第 3 项到结尾的值.数据缓冲区必须表现良好.

void fillwithscalar(void *buffer, npy_intp length, void *value, void *arr)#

指向一个函数的指针,该函数用给定的标量 value 填充给定 length 的连续 buffer ,该标量的地址已给出.最后一个参数是数组,需要该数组来获取可变长度数组的 itemsize.

int sort(void *start, npy_intp length, void *arr)#

一个指向特定排序算法的函数指针数组.使用键获得特定的排序算法(目前定义了 NPY_QUICKSORT , NPY_HEAPSORTNPY_MERGESORT ).这些排序是就地完成的,假设数据是连续且对齐的.

int argsort(void *start, npy_intp *result, npy_intp length, void *arr)#

一个指向该数据类型的排序算法的函数指针数组.与 sort 相同的排序算法可用.产生排序的索引在 result 中返回(必须使用包含 0 到 length-1 的索引初始化).

PyObject *castdict#

NULL 或包含用户定义数据类型的底层强制转换函数的字典.每个函数都封装在 PyCapsule* 中,并以数据类型编号作为键.

NPY_SCALARKIND scalarkind(PyArrayObject *arr)#

用于确定应如何解释此类型的标量的函数.参数是 NULL 或包含数据的 0 维数组(如果需要确定标量的种类).返回值必须是 NPY_SCALARKIND 类型.

int **cancastscalarkindto#

NULLNPY_NSCALARKINDS 指针的数组. 这些指针中的每一个都应该是 NULL 或指向整数数组的指针(以 NPY_NOTYPE 结尾),指示可以安全地将指定类型的此数据类型的标量强制转换为的数据类型(这通常意味着不会丢失精度).

int *cancastto#

NULL 或整数数组(以 NPY_NOTYPE 结尾),指示可以安全地将此数据类型强制转换为的数据类型(这通常意味着不会丢失精度).

int argmin(void *data, npy_intp n, npy_intp *min_ind, void *arr)#

一个指向函数的指针,该函数检索 arrn 个元素的最小值索引,从 data 指向的元素开始.此函数要求内存段是连续且表现良好.返回值始终为 0.最小元素的索引在 min_ind 中返回.

PyArrayMethod_Context 和 PyArrayMethod_Spec#

type PyArrayMethodObject_tag#

一个不透明的结构,用于表示 ArrayMethod 循环中的方法“self”.

type PyArrayMethod_Context#

一个传递到 ArrayMethod 循环中的结构,用于为循环的运行时使用提供上下文.

typedef struct {
    PyObject *caller;
    struct PyArrayMethodObject_tag *method;
    PyArray_Descr *const *descriptors;
} PyArrayMethod_Context
PyObject *caller#

调用者,通常是调用循环的ufunc.当调用不是来自ufunc时(例如,强制转换),可能为 NULL .

struct PyArrayMethodObject_tag *method#

方法“self”.目前,此对象是不透明指针.

PyArray_Descr **descriptors#

ufunc 循环的描述符数组,由 resolve_descriptors 填充.数组的长度是 nin + nout .

type PyArrayMethod_Spec#

一个用于使用 NumPy 注册 ArrayMethod 的结构.我们使用 Python 限制 API 使用的插槽机制.有关插槽定义,请参见下文.

typedef struct {
   const char *name;
   int nin, nout;
   NPY_CASTING casting;
   NPY_ARRAYMETHOD_FLAGS flags;
   PyArray_DTypeMeta **dtypes;
   PyType_Slot *slots;
} PyArrayMethod_Spec;
const char *name#

循环的名称.

int nin#

输入操作数的数量

int nout#

输出操作数的数量.

NPY_CASTING casting#

用于指示强制转换操作应具有的最低许可程度.例如,如果强制转换操作在某些情况下可能是安全的,但在其他情况下可能是不安全的,则应设置 NPY_UNSAFE_CASTING .不用于ufunc循环,但仍必须设置.

NPY_ARRAYMETHOD_FLAGS flags#

为该方法设置的标志.

PyArray_DTypeMeta **dtypes#

循环的 DType.长度必须为 nin + nout .

PyType_Slot *slots#

该方法的槽数组.槽 ID 必须是以下值之一.

PyArray_DTypeMeta 和 PyArrayDTypeMeta_Spec#

PyTypeObject PyArrayDTypeMeta_Type#

对应于 PyArray_DTypeMeta 的 Python 类型对象.

type PyArray_DTypeMeta#

一个主要是不透明的结构,表示 DType 类.每个实例都为单个 NumPy 数据类型定义一个元类.数据类型可以是参数化的或非参数化的.对于非参数化类型,DType 类与从 DType 类创建的描述符实例具有一对一的对应关系.参数化类型可以对应于许多不同的 dtype 实例,具体取决于所选的参数.此类型可在公共的 numpy/dtype_api.h 标头中使用.目前,有限的 CPython API 不支持使用此结构,因此如果设置了 Py_LIMITED_API ,则此类型是 PyTypeObject 的 typedef.

typedef struct {
     PyHeapTypeObject super;
     PyArray_Descr *singleton;
     int type_num;
     PyTypeObject *scalar_type;
     npy_uint64 flags;
     void *dt_slots;
     void *reserved[3];
} PyArray_DTypeMeta
PyHeapTypeObject super#

超类,提供与 Python 对象 API 的挂钩.设置此结构的成员以填充实现 PyTypeObject API 的函数(例如 tp_new ).

PyArray_Descr *singleton#

一个适合用作数据类型单例描述符的描述符实例.这对于表示简单的旧数据类型的非参数化类型很有用,其中所有该类型的数据只有一个逻辑描述符实例.如果单例实例不合适,则可以为 NULL.

int type_num#

对应于旧数据类型的类型编号.在 NumPy 之外定义的数据类型以及 NumPy 可能附带的未来数据类型会将 type_num 设置为 -1,因此不应依赖此值来区分数据类型.

PyTypeObject *scalar_type#

此数据类型的标量实例的类型.

npy_uint64 flags#

可以设置标志以向 NumPy 指示此数据类型具有可选行为.有关允许的标志值列表,请参见 标志 .

void *dt_slots#

指向私有结构的不透明指针,该结构包含 DType API 中函数的实现.这是从用于初始化 DType 的 PyArrayDTypeMeta_Spec 实例的 slots 成员中填充的.

type PyArrayDTypeMeta_Spec#

用于使用 PyArrayInitDTypeMeta_FromSpec 函数初始化新 DType 的结构.

typedef struct {
    PyTypeObject *typeobj;
    int flags;
    PyArrayMethod_Spec **casts;
    PyType_Slot *slots;
    PyTypeObject *baseclass;
}
PyTypeObject *typeobj#

可以是 NULL 或与 DType 关联的 python 标量的类型.对数组进行标量索引会返回一个具有此类型的项目.

int flags#

DType 类的静态标志,指示 DType 是否是参数化的,抽象的或表示数值数据.后者是可选的,但如果设置为指示下游代码 DType 表示数字(整数,浮点数或其他数值数据类型)或其他内容(例如字符串,单位或日期),则很有用.

PyArrayMethod_Spec **casts;#

由 DType 定义的转换的 ArrayMethod 规范的 NULL 终止数组.

PyType_Slot *slots;#

DType API 中函数的实现的槽规范的 NULL 终止数组.槽 ID 必须是 槽 ID 和 API 函数类型定义 中枚举的 DType 槽 ID 之一.

公开的 DType 类( PyArray_DTypeMeta 对象)#

为了与 promoter 一起使用,NumPy 公开了一些遵循 np.dtypes 中发现的模式 PyArray_<Name>DType 的 Dtype.

此外,三个 DType: PyArray_PyLongDType , PyArray_PyFloatDType , PyArray_PyComplexDType 对应于 Python 标量值. 这些不能在所有位置使用,但确实允许常见的 dtype 操作,并且使用它们实现 promotion 可能是必要的.

此外,定义了以下抽象 DType,它们涵盖了内置的 NumPy 和 python DType,并且用户原则上可以从中继承(这不会继承任何 DType 特定功能): * PyArray_IntAbstractDType * PyArray_FloatAbstractDType * PyArray_ComplexAbstractDType

警告

从 NumPy 2.0 开始,这些 DType 的唯一有效用途是方便地注册一个 promoter,例如匹配“任何整数”(以及子类检查).因此,它们不会暴露给 Python.

PyUFunc_Type 和 PyUFuncObject#

PyTypeObject PyUFunc_Type#

ufunc 对象通过创建 PyUFunc_Type 来实现.它是一种非常简单的类型,仅实现基本的 getattribute 行为,打印行为,并且具有允许这些对象像函数一样运行的调用行为.ufunc 背后的基本思想是保存对支持该操作的每种数据类型的快速一维(向量)循环的引用.这些一维循环都具有相同的签名,是创建新 ufunc 的关键.它们由通用循环代码根据需要调用,以实现 N 维函数.对于浮点和复数浮点数组,还定义了一些通用的一维循环,允许您使用单个标量函数定义 ufunc(例如 atanh).

type PyUFuncObject#

ufunc 的核心是 PyUFuncObject ,它包含调用执行实际工作的底层 C 代码循环所需的所有信息.虽然为了完整性在此处进行了描述,但应将其视为 NumPy 的内部对象,并通过 PyUFunc_* 函数进行操作.此结构的大小可能会因 NumPy 版本而异.为了确保兼容性:

  • 永远不要声明该结构的非指针实例

  • 永远不要执行指针运算

  • 永远不要使用 sizeof(PyUFuncObject)

它具有以下结构:

typedef struct {
    PyObject_HEAD
    int nin;
    int nout;
    int nargs;
    int identity;
    PyUFuncGenericFunction *functions;
    void **data;
    int ntypes;
    int reserved1;
    const char *name;
    char *types;
    const char *doc;
    void *ptr;
    PyObject *obj;
    PyObject *userloops;
    int core_enabled;
    int core_num_dim_ix;
    int *core_num_dims;
    int *core_dim_ixs;
    int *core_offsets;
    char *core_signature;
    PyUFunc_TypeResolutionFunc *type_resolver;
    void *reserved2;
    void *reserved3;
    npy_uint32 *op_flags;
    npy_uint32 *iter_flags;
    /* new in API version 0x0000000D */
    npy_intp *core_dim_sizes;
    npy_uint32 *core_dim_flags;
    PyObject *identity_value;
    /* Further private slots (size depends on the NumPy version) */
} PyUFuncObject;
int nin#

输入参数的数量.

int nout#

输出参数的数量.

int nargs#

参数总数 (nin + nout).必须小于 NPY_MAXARGS .

int identity#

PyUFunc_One , PyUFunc_Zero , PyUFunc_MinusOne , PyUFunc_None , PyUFunc_ReorderableNonePyUFunc_IdentityValue 之一,用于指示此操作的单位元.它仅用于对空数组的类似 reduce 的调用.

void functions(char **args, npy_intp *dims, npy_intp *steps, void *extradata)#

函数指针数组—每个 ufunc 支持的数据类型各一个.这是向量循环,被调用以 dims [0] 次实现底层函数.第一个参数 args 是一个包含 nargs 指向 behaved 内存的指针的数组.指向输入参数数据的指针在前,然后是指向输出参数数据的指针.必须跳过多少字节才能到达序列中的下一个元素由 steps 数组中的相应条目指定.最后一个参数允许循环接收额外信息.这通常用于使单个通用向量循环可用于多个函数.在这种情况下,要调用的实际标量函数作为 extradata 传入.此函数指针数组的大小为 ntypes.

void **data#

要传递给一维向量循环的额外数据,如果不需要额外数据,则为 NULL .此 C 数组的大小必须与 functions 数组的大小相同(即 ntypes).如果不需要 extra_data,则使用 NULL .UFunc 的几个 C-API 调用只是利用此额外数据来接收指向要调用的实际函数的指针的一维向量循环.

int ntypes#

ufunc 支持的数据类型数量.此数字指定有多少不同的 1-d 循环(内置数据类型)可用.

char *name#

ufunc 的字符串名称.这用于动态构建 ufunc 的 __doc__ 属性.

char *types#

一个 \(nargs \times ntypes\) 的 8 位 type_numbers 数组,其中包含每个受支持的(内置)数据类型的函数类型签名.对于 ntypes 函数中的每一个,此数组中相应的类型编号集显示 args 参数应如何在一维向量循环中解释.这些类型编号不必是相同的类型,并且支持混合类型 ufunc.

char *doc#

ufunc 的文档.不应包含函数签名,因为在检索 __doc__ 时会动态生成该签名.

void *ptr#

任何动态分配的内存.目前,这用于从 Python 函数创建的动态 ufunc,以存储类型,数据和名称成员的空间.

PyObject *obj#

对于从 Python 函数动态创建的 ufunc,此成员保存对底层 Python 函数的引用.

PyObject *userloops#

用户定义类型的一维向量循环字典(存储为 CObject 指针).用户可以为任何用户定义类型注册循环.它通过类型编号检索.用户定义的类型编号始终大于 NPY_USERDEF .

int core_enabled#

0 表示标量 ufunc;1 表示广义 ufunc

int core_num_dim_ix#

签名中不同核心维度名称的数量

int *core_num_dims#

每个参数的核心维度数

int *core_dim_ixs#

扁平形式的维度索引;参数 k 的索引存储在 core_dim_ixs[core_offsets[k] : core_offsets[k] + core_numdims[k]]

int *core_offsets#

每个参数的第一个核心维度在 core_dim_ixs 中的位置,等同于 cumsum( core_num_dims )

char *core_signature#

核心签名字符串

PyUFunc_TypeResolutionFunc *type_resolver#

一个函数,用于解析类型并填充一个包含输入和输出的 dtypes 的数组

type PyUFunc_TypeResolutionFunc#

type_resolver 的函数指针类型

npy_uint32 op_flags#

覆盖每个 ufunc 操作数的默认操作数标志.

npy_uint32 iter_flags#

覆盖 ufunc 的默认 nditer 标志.

在 API 版本 0x0000000D 中添加

npy_intp *core_dim_sizes#

对于每个不同的核心维度,如果 frozen0 ,则可能的 UFUNC_CORE_DIM_SIZE_INFERRED 大小

npy_uint32 *core_dim_flags#

对于每个不同的核心维度,一组标志( UFUNC_CORE_DIM_CAN_IGNOREUFUNC_CORE_DIM_SIZE_INFERRED )

PyObject *identity_value#

归约的恒等值,当 PyUFuncObject.identity 等于 PyUFunc_IdentityValue 时.

UFUNC_CORE_DIM_CAN_IGNORE#

如果 dim 名称以 ? 结尾

UFUNC_CORE_DIM_SIZE_INFERRED#

如果 dim 大小将从操作数确定,而不是从 frozen 签名确定

PyArrayIter_Type 和 PyArrayIterObject#

PyTypeObject PyArrayIter_Type#

这是一个迭代器对象,可以轻松地循环遍历 N 维数组.它是从 ndarray 的 flat 属性返回的对象.它也广泛用于整个实现内部,以循环遍历 N 维数组.实现了 tp_as_mapping 接口,以便可以索引迭代器对象(使用一维索引),并且通过 tp_methods 表实现了一些方法.此对象实现了 next 方法,并且可以在 Python 中使用迭代器的任何地方使用.

type PyArrayIterObject#

PyArrayIter_Type 对象相对应的 C 结构是 PyArrayIterObject . PyArrayIterObject 用于跟踪指向 N 维数组的指针.它包含用于快速遍历数组的相关信息.可以通过三种基本方式调整指针:1) 以 C 风格的连续方式前进到数组中的“下一个”位置,2) 前进到数组中的任意 N 维坐标,以及 3) 前进到数组中的任意一维索引. PyArrayIterObject 结构的成员用于这些计算.迭代器对象保留其自身的关于数组的维度和步幅信息.可以根据需要调整此信息以进行“广播”,或仅循环遍历特定维度.

typedef struct {
    PyObject_HEAD
    int   nd_m1;
    npy_intp  index;
    npy_intp  size;
    npy_intp  coordinates[NPY_MAXDIMS_LEGACY_ITERS];
    npy_intp  dims_m1[NPY_MAXDIMS_LEGACY_ITERS];
    npy_intp  strides[NPY_MAXDIMS_LEGACY_ITERS];
    npy_intp  backstrides[NPY_MAXDIMS_LEGACY_ITERS];
    npy_intp  factors[NPY_MAXDIMS_LEGACY_ITERS];
    PyArrayObject *ao;
    char  *dataptr;
    npy_bool  contiguous;
} PyArrayIterObject;
int nd_m1#

\(N-1\) ,其中 \(N\) 是底层数组中的维数.

npy_intp index#

当前的一维索引到数组中.

npy_intp size#

底层数组的总大小.

npy_intp *coordinates#

到数组中的一个 \(N\) 维索引.

npy_intp *dims_m1#

数组在每个维度中减 1 的大小.

npy_intp *strides#

数组的步长.表示在每个维度中,跳转到下一个元素所需的字节数.

npy_intp *backstrides#

表示从一个维度的末尾跳转回其开头所需的字节数.注意, backstrides[k] == strides[k] * dims_m1[k] ,但为了优化,此处存储了该值.

npy_intp *factors#

此数组用于从一维索引计算 N 维索引.它包含所需维度的乘积.

PyArrayObject *ao#

指向底层 ndarray 的指针,此迭代器被创建以表示该 ndarray.

char *dataptr#

此成员指向由索引指示的 ndarray 中的一个元素.

npy_bool contiguous#

如果基础数组是 NPY_ARRAY_C_CONTIGUOUS ,则此标志为真.它用于简化可能的计算.

如何在 C 级使用数组迭代器将在后面的章节中更全面地解释.通常,您不需要关心迭代器对象的内部结构,只需通过使用宏 PyArray_ITER_NEXT (it), PyArray_ITER_GOTO (it, dest), 或 PyArray_ITER_GOTO1D (it, index) 与其交互.所有这些宏都需要参数 it 为 PyArrayIterObject* .

PyArrayMultiIter_Type 和 PyArrayMultiIterObject#

PyTypeObject PyArrayMultiIter_Type#

此类型提供了一个封装了广播概念的迭代器.它允许 \(N\) 个数组一起广播,使得循环以 C 风格的连续方式在广播后的数组上进行.相应的 C 结构是 PyArrayMultiIterObject ,其内存布局必须从传递给 PyArray_Broadcast (obj) 函数的任何对象 obj 开始.广播通过调整数组迭代器来实现,使得每个迭代器表示广播后的形状和大小,但其步长被调整,以便在每次迭代中使用来自数组的正确元素.

type PyArrayMultiIterObject#
typedef struct {
    PyObject_HEAD
    int numiter;
    npy_intp size;
    npy_intp index;
    int nd;
    npy_intp dimensions[NPY_MAXDIMS_LEGACY_ITERS];
    PyArrayIterObject *iters[];
} PyArrayMultiIterObject;
int numiter#

需要广播到相同形状的数组的数量.

npy_intp size#

总的广播后的大小.

npy_intp index#

当前(一维)索引到广播后的结果中.

int nd#

广播结果中的维度数.

npy_intp *dimensions#

广播结果的形状(仅使用 nd 插槽).

PyArrayIterObject **iters#

一个迭代器对象数组,保存要一起广播的数组的迭代器.返回时,迭代器会针对广播进行调整.

PyArrayNeighborhoodIter_Type 和 PyArrayNeighborhoodIterObject#

PyTypeObject PyArrayNeighborhoodIter_Type#

这是一个迭代器对象,可以轻松地遍历 N 维邻域.

type PyArrayNeighborhoodIterObject#

对应于 PyArrayNeighborhoodIter_Type 对象的 C 结构是 PyArrayNeighborhoodIterObject .

typedef struct {
    PyObject_HEAD
    int nd_m1;
    npy_intp index, size;
    npy_intp coordinates[NPY_MAXDIMS_LEGACY_ITERS]
    npy_intp dims_m1[NPY_MAXDIMS_LEGACY_ITERS];
    npy_intp strides[NPY_MAXDIMS_LEGACY_ITERS];
    npy_intp backstrides[NPY_MAXDIMS_LEGACY_ITERS];
    npy_intp factors[NPY_MAXDIMS_LEGACY_ITERS];
    PyArrayObject *ao;
    char *dataptr;
    npy_bool contiguous;
    npy_intp bounds[NPY_MAXDIMS_LEGACY_ITERS][2];
    npy_intp limits[NPY_MAXDIMS_LEGACY_ITERS][2];
    npy_intp limits_sizes[NPY_MAXDIMS_LEGACY_ITERS];
    npy_iter_get_dataptr_t translate;
    npy_intp nd;
    npy_intp dimensions[NPY_MAXDIMS_LEGACY_ITERS];
    PyArrayIterObject* _internal_iter;
    char* constant;
    int mode;
} PyArrayNeighborhoodIterObject;

ScalarArrayTypes#

对于数组中可能存在的每种不同的内置数据类型,都有一个 Python 类型.其中大多数都是 C 中相应数据类型的简单包装器.这些类型的 C 名称为 Py{TYPE}ArrType_Type ,其中 {TYPE} 可以是

Bool, Byte, Short, Int, Long, LongLong, UByte, UShort, UInt, ULong, ULongLong, Half, Float, Double, LongDouble, CFloat, CDouble, CLongDouble, String, Unicode, Void, Datetime, Timedelta, 和 Object.

这些类型名称是 C-API 的一部分,因此可以在扩展 C 代码中创建.还有 PyIntpArrType_TypePyUIntpArrType_Type ,它们是可以在平台上保存指针的整数类型的简单替代品.这些标量对象的结构不会暴露给 C 代码.函数 PyArray_ScalarAsCtype (..) 可用于从数组标量中提取 C 类型值,函数 PyArray_Scalar (…) 可用于从 C 值构造数组标量.

其他的 C 结构体#

在 NumPy 的开发中,发现了一些新的 C 结构体很有用.这些 C 结构体在至少一个 C-API 调用中使用,因此在此处进行文档记录.定义这些结构体的主要原因是方便使用 Python ParseTuple C-API 从 Python 对象转换为有用的 C 对象.

PyArray_Dims#

type PyArray_Dims#

当需要解释形状和/或步长信息时,此结构体非常有用.该结构体是:

typedef struct {
    npy_intp *ptr;
    int len;
} PyArray_Dims;

此结构的成员有

npy_intp *ptr#

指向 ( npy_intp ) 整数列表的指针,该列表通常表示数组形状或数组步长.

int len#

整数列表的长度.假设可以安全地访问 ptr [0] 到 ptr [len-1].

PyArray_Chunk#

type PyArray_Chunk#

这相当于 Python 中的 buffer 对象结构,直到 ptr 成员.在 32 位平台 (即如果 NPY_SIZEOF_INT == NPY_SIZEOF_INTP ) 上,len 成员也与 buffer 对象的等效成员匹配.它可用于表示通用的单段内存块.

typedef struct {
    PyObject_HEAD
    PyObject *base;
    void *ptr;
    npy_intp len;
    int flags;
} PyArray_Chunk;

成员有

PyObject *base#

此内存块来自的 Python 对象.需要这样才能正确计算内存.

void *ptr#

指向单段内存块起点的指针.

npy_intp len#

段的长度(以字节为单位).

int flags#

应用于解释内存的任何数据标志(例如 NPY_ARRAY_WRITEABLE ).

PyArrayInterface#

type PyArrayInterface#

定义 PyArrayInterface 结构是为了使 NumPy 和其他扩展模块可以使用快速数组接口协议.支持快速数组接口协议的对象的 __array_struct__ 方法应返回一个 PyCapsule ,其中包含指向 PyArrayInterface 结构的指针,该结构包含数组的相关详细信息.创建新数组后,应 DECREF 该属性,这将释放 PyArrayInterface 结构.记住 INCREF 对象(其 __array_struct__ 属性已被检索),并将新 PyArrayObject 的 base 成员指向同一个对象.这样,数组的内存将得到正确管理.

typedef struct {
    int two;
    int nd;
    char typekind;
    int itemsize;
    int flags;
    npy_intp *shape;
    npy_intp *strides;
    void *data;
    PyObject *descr;
} PyArrayInterface;
int two#

作为健全性检查的整数 2.

int nd#

数组中的维数.

char typekind#

一个字符,根据类型字符串约定指示存在的数组类型,其中 ‘t’ -> 位域,’b’ -> 布尔值,’i’ -> 有符号整数,’u’ -> 无符号整数,’f’ -> 浮点数,’c’ -> 复数浮点数,’O’ -> 对象,’S’ -> (字节-)字符串,’U’ -> unicode,’V’ -> void.

int itemsize#

数组中每个项目所需的字节数.

int flags#

NPY_ARRAY_C_CONTIGUOUS (1), NPY_ARRAY_F_CONTIGUOUS (2), NPY_ARRAY_ALIGNED (0x100), NPY_ARRAY_NOTSWAPPED (0x200) 或 NPY_ARRAY_WRITEABLE (0x400) 中的任何一位都用于指示有关数据的信息.实际上, NPY_ARRAY_ALIGNED , NPY_ARRAY_C_CONTIGUOUSNPY_ARRAY_F_CONTIGUOUS 标志可以从其他参数确定.还可以设置标志 NPY_ARR_HAS_DESCR (0x800),以向使用版本 3 数组接口的对象指示结构的 descr 成员存在(使用数组接口版本 2 的对象将忽略它).

npy_intp *shape#

一个数组,包含数组在每个维度中的大小.

npy_intp *strides#

一个数组,包含跳转到每个维度中的下一个元素所需的字节数.

void *data#

指向数组第一个元素的指针.

PyObject *descr#

一个更详细描述数据类型(与 __array_interface__ 中的 descr 键相同)的 Python 对象.如果 typekind 和 itemsize 提供了足够的信息,则可以为 NULL . 除非 flags 中的 NPY_ARR_HAS_DESCR 标志为 on,否则此字段也会被忽略.

内部使用的结构#

在内部,代码使用一些额外的 Python 对象,主要用于内存管理. 这些类型不能直接从 Python 访问,也不会暴露给 C-API. 这里包含它们只是为了完整性和帮助理解代码.

type PyUFunc_Loop1d#

一个简单的 C 结构链表,包含定义一个 ufunc 的 1-d 循环所需的信息,用于用户定义的数据类型的每个定义的签名.

PyTypeObject PyArrayMapIter_Type#

高级索引由这个 Python 类型处理. 它只是一个围绕 C 结构的松散包装器,其中包含高级数组索引所需的变量.

type PyArrayMapIterObject#

PyArrayMapIter_Type 关联的 C 结构. 如果您试图理解高级索引映射代码,此结构很有用. 它在 arrayobject.h 标头中定义. 此类型不暴露给 Python,可以用 C 结构替换. 作为 Python 类型,它利用了引用计数的内存管理.

NumPy C-API 和 C 复数#

当您使用 NumPy C-API 时,您将可以访问复数实数声明 npy_cdoublenpy_cfloat ,它们是根据 complex.h 中的 C 标准类型声明的.不幸的是, complex.h 包含 #define I … ``(实际定义取决于编译器),这意味着任何下游用户执行 ` #include <numpy/arrayobject.h> `` 可能会得到 `` I `` 的定义,并且在他们的代码中使用类似声明 `` double I; `` 可能会导致晦涩的编译器错误,例如

可以通过添加以下内容来避免此错误:

#undef I

到您的代码中.

在 2.0 版本发生变更: 包含`` complex.h 是NumPy 2中的新特性,因此定义不同 I 的代码可能不需要旧版本上的 #undef I ``.NumPy 2.0.1 曾简要包含 `` #under I ``