Python类型和C结构#
在C代码中定义了几种新类型.其中大多数可以从Python访问,但由于其有限的用途,一些没有公开.每个新的Python类型都有一个关联的 PyObject* ,其内部结构包含一个指向“方法表”的指针,该表定义了新对象在Python中的行为方式.当您在C代码中接收到Python对象时,您总是会获得一个指向 PyObject 结构的指针.因为 PyObject 结构非常通用,并且只定义了 PyObject_HEAD ,所以它本身并不是很有趣.但是,不同的对象在 PyObject_HEAD 之后包含更多细节(但您必须强制转换为正确的类型才能访问它们—或者使用访问器函数或宏).
定义的新Python类型#
Python类型在C中与Python中的类在功能上等效.通过构造一个新的Python类型,您可以为Python提供一个新对象.ndarray对象是在C中定义的新类型的一个例子.通过两个基本步骤在C中定义新类型:
创建一个C结构体(通常命名为
Py{Name}Object),该结构体与PyObject结构体本身在二进制上兼容,但包含该特定对象所需的额外信息;使用指向函数的指针填充
PyTypeObject表(由PyObject结构的 ob_type 成员指向),这些函数实现该类型所需的行为.
与定义 Python 类行为的特殊方法名不同,这里有“函数表”,其中包含指向实现所需结果的函数的指针.PyTypeObject 本身是动态的,这允许 C 类型从 C 中的其他 C 类型“子类型化”,并在 Python 中进行子类化.子类型继承其父类型的属性和方法.
有两个主要的新类型:ndarray ( PyArray_Type ) 和 ufunc ( PyUFunc_Type ).其他类型起辅助作用: PyArrayIter_Type , PyArrayMultiIter_Type 和 PyArrayDescr_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#
PyArrayObjectC 结构体包含数组的所有必需信息.ndarray(及其子类)的所有实例都将具有此结构.为了将来的兼容性,通常应使用提供的宏访问这些结构成员.如果需要更短的名称,则可以使用定义为等效于NPY_AO的PyArrayObject(已弃用).直接访问结构字段已被弃用.请改用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.
备注
进一步的成员被认为是私有的并且依赖于版本.如果 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_mapping 和 tp_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.genericpython 类型的 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_GETITEM和NPY_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_ELSIZE 和 PyDataType_SET_ELSIZE ,以获得一种与 NumPy 1.x 兼容的方式来访问此字段.
-
npy_intp alignment#
一个提供此数据类型对齐信息的数字.具体来说,它显示了编译器将此类型的项目放置在距2元素结构(其第一个元素是
char)开头多远的位置:offsetof(struct {char c; type v;}, v)请参阅 PyDataType_ALIGNMENT ,以获得一种与 NumPy 1.x 兼容的方式来访问此字段.
-
NpyAuxData *c_metadata#
特定于特定 dtype 的 C 实现的元数据.为 NumPy 1.7.0 添加.
-
type npy_hash_t#
-
npy_hash_t *hash#
用于缓存哈希值.
-
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,getitem和cast.这些函数指针被假定为非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,getitem和setitem函数可以(并且必须)处理行为不端的数组.其他函数需要行为良好的内存段.备注
这些函数主要是旧版 API,但是,有些函数仍然在使用.从 NumPy 2.x 开始,它们只能通过 PyDataType_GetArrFuncs 访问(有关更多详细信息,请参见该函数).在使用结构中定义的任何函数之前,应检查它是否为
NULL.通常,可以预期定义了函数getitem,setitem,copyswap和copyswapn,但是预计所有函数都将被更新的 API 替换.例如,PyArray_Pack是setitem的更强大版本,例如,它可以正确处理强制类型转换.-
void cast(void *from, void *to, npy_intp n, void *fromarr, void *toarr)#
一个函数指针数组,用于将当前类型转换为所有其他内置类型.每个函数将
from指向的连续,对齐且未交换的缓冲区转换为to指向的连续,对齐且未交换的缓冲区.要转换的条目数由n给出,参数fromarr和toarr被解释为 PyArrayObjects ,用于灵活数组以获取项目大小信息.
-
PyObject *getitem(void *data, void *arr)#
指向一个函数的指针,该函数从数组对象
arr由data指向的单个元素返回一个标准的 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_UNICODE和NPY_VOID)数组(并从arr->descr->elsize获取).第二个函数复制单个值,而第一个函数使用提供的步幅循环遍历n个值.这些函数可以处理行为不端的src数据.如果src为 NULL,则不执行复制.如果swap为 0,则不进行字节交换.假设dest和src不重叠.如果它们重叠,则首先使用memmove(…),然后使用 NULL 值的src进行copyswap(n).
-
int compare(const void *d1, const void *d2, void *arr)#
指向一个函数的指针,该函数比较数组
arr的两个元素,由d1和d2指向.此函数需要行为良好(对齐且未交换)的数组.如果 *d1> *d2,则返回值为 1;如果 *d1== *d2,则返回值为 0;如果 *d1< *d2,则返回值为 -1.数组对象arr用于检索灵活数组的项目大小和字段信息.
-
int argmax(void *data, npy_intp n, npy_intp *max_ind, void *arr)#
指向一个函数的指针,该函数检索从由
data指向的元素开始的arr中n个元素中最大的元素的索引.此函数要求内存段是连续且行为良好的.返回值始终为 0.最大元素的索引在max_ind中返回.
-
void dotfunc(void *ip1, npy_intp is1, void *ip2, npy_intp is2, void *op, npy_intp n, void *arr)#
指向一个函数的指针,该函数将两个长度为
n的序列相乘,将它们相加,然后将结果放入arr的op指向的元素中.两个序列的起始位置分别由ip1和ip2指向.要到达每个序列中的下一个元素,分别需要跳跃is1和is2字节.此函数需要行为良好(尽管不一定是连续的)的内存.
-
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指向字符串的其余部分.最后一个参数arr是ip指向的数组(可变大小数据类型需要).成功时返回 0,失败时返回 -1.需要行为良好的数组.应该在不持有 Python GIL 的情况下调用此函数,并且必须获取它以进行错误报告.
-
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_HEAPSORT和NPY_MERGESORT).这些排序是就地完成的,假设数据是连续且对齐的.
-
int argsort(void *start, npy_intp *result, npy_intp length, void *arr)#
一个指向该数据类型的排序算法的函数指针数组.与 sort 相同的排序算法可用.产生排序的索引在
result中返回(必须使用包含 0 到length-1的索引初始化).
-
NPY_SCALARKIND scalarkind(PyArrayObject *arr)#
用于确定应如何解释此类型的标量的函数.参数是
NULL或包含数据的 0 维数组(如果需要确定标量的种类).返回值必须是NPY_SCALARKIND类型.
-
int **cancastscalarkindto#
NULL或NPY_NSCALARKINDS指针的数组. 这些指针中的每一个都应该是NULL或指向整数数组的指针(以NPY_NOTYPE结尾),指示可以安全地将指定类型的此数据类型的标量强制转换为的数据类型(这通常意味着不会丢失精度).
-
int *cancastto#
NULL或整数数组(以NPY_NOTYPE结尾),指示可以安全地将此数据类型强制转换为的数据类型(这通常意味着不会丢失精度).
-
void cast(void *from, void *to, npy_intp n, void *fromarr, void *toarr)#
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
-
struct PyArrayMethodObject_tag *method#
方法“self”.目前,此对象是不透明指针.
-
PyArray_Descr **descriptors#
ufunc 循环的描述符数组,由
resolve_descriptors填充.数组的长度是nin+nout.
-
struct PyArrayMethodObject_tag *method#
-
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 必须是以下值之一.
-
const char *name#
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 的挂钩.设置此结构的成员以填充实现
PyTypeObjectAPI 的函数(例如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成员中填充的.
-
PyHeapTypeObject super#
-
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 之一.
-
PyTypeObject *typeobj#
公开的 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_ReorderableNone或PyUFunc_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 *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的函数指针类型
-
type PyUFunc_TypeResolutionFunc#
-
npy_uint32 op_flags#
覆盖每个 ufunc 操作数的默认操作数标志.
-
npy_uint32 iter_flags#
覆盖 ufunc 的默认 nditer 标志.
在 API 版本 0x0000000D 中添加
-
npy_intp *core_dim_sizes#
对于每个不同的核心维度,如果 frozen 为
0,则可能的UFUNC_CORE_DIM_SIZE_INFERRED大小
-
npy_uint32 *core_dim_flags#
对于每个不同的核心维度,一组标志(
UFUNC_CORE_DIM_CAN_IGNORE和UFUNC_CORE_DIM_SIZE_INFERRED)
-
PyObject *identity_value#
归约的恒等值,当
PyUFuncObject.identity等于PyUFunc_IdentityValue时.
-
UFUNC_CORE_DIM_CAN_IGNORE#
如果 dim 名称以
?结尾
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 *backstrides#
表示从一个维度的末尾跳转回其开头所需的字节数.注意,
backstrides[k] == strides[k] * dims_m1[k],但为了优化,此处存储了该值.
-
PyArrayObject *ao#
指向底层 ndarray 的指针,此迭代器被创建以表示该 ndarray.
-
char *dataptr#
此成员指向由索引指示的 ndarray 中的一个元素.
-
npy_bool contiguous#
如果基础数组是
NPY_ARRAY_C_CONTIGUOUS,则此标志为真.它用于简化可能的计算.
-
int nd_m1#
如何在 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#
需要广播到相同形状的数组的数量.
-
int nd#
广播结果中的维度数.
-
PyArrayIterObject **iters#
一个迭代器对象数组,保存要一起广播的数组的迭代器.返回时,迭代器会针对广播进行调整.
-
int numiter#
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_Type 和 PyUIntpArrType_Type ,它们是可以在平台上保存指针的整数类型的简单替代品.这些标量对象的结构不会暴露给 C 代码.函数 PyArray_ScalarAsCtype (..) 可用于从数组标量中提取 C 类型值,函数 PyArray_Scalar (…) 可用于从 C 值构造数组标量.
其他的 C 结构体#
在 NumPy 的开发中,发现了一些新的 C 结构体很有用.这些 C 结构体在至少一个 C-API 调用中使用,因此在此处进行文档记录.定义这些结构体的主要原因是方便使用 Python ParseTuple C-API 从 Python 对象转换为有用的 C 对象.
PyArray_Dims#
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;
成员有
-
void *ptr#
指向单段内存块起点的指针.
-
int flags#
应用于解释内存的任何数据标志(例如
NPY_ARRAY_WRITEABLE).
-
void *ptr#
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_CONTIGUOUS和NPY_ARRAY_F_CONTIGUOUS标志可以从其他参数确定.还可以设置标志NPY_ARR_HAS_DESCR(0x800),以向使用版本 3 数组接口的对象指示结构的 descr 成员存在(使用数组接口版本 2 的对象将忽略它).
-
void *data#
指向数组第一个元素的指针.
-
PyObject *descr#
一个更详细描述数据类型(与
__array_interface__中的 descr 键相同)的 Python 对象.如果 typekind 和 itemsize 提供了足够的信息,则可以为NULL. 除非 flags 中的NPY_ARR_HAS_DESCR标志为 on,否则此字段也会被忽略.
-
int two#
内部使用的结构#
在内部,代码使用一些额外的 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_cdouble 和 npy_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 ``