python类型和C结构

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

定义了新的python类型

python类型在python类的C中是功能等价的。通过构造新的python类型,可以为python提供新的对象。ndarray对象是在C中定义的新类型的一个示例。新类型在C中通过两个基本步骤定义:

  1. 创建C结构(通常命名为 Py{{Name}}Object )它是二进制的-与 PyObject 结构本身,但保留该特定对象所需的附加信息;

  2. 填充 PyTypeObject 表(由 PyObject 结构)具有指向实现类型所需行为的函数的指针。

与定义Python类行为的特殊方法名不同,还有“函数表”指向实现所需结果的函数。自python 2.2以来,pytypeobject本身已经成为动态的,它允许C类型可以从C中的其他C类型“子类型化”,并在python中进行子类化。子类型从其父类型继承属性和方法。

有两种主要的新类型:ndarray( PyArray_Type )和不明飞行物( PyUFunc_Type )其他类型起到支持作用: PyArrayIter_Type , the PyArrayMultiIter_TypePyArrayDescr_Type . 这个 PyArrayIter_Type 是ndarray的平面迭代器的类型(获取平面属性时返回的对象)。这个 PyArrayMultiIter_Type 调用时返回的对象类型 broadcast ()它处理嵌套序列集合上的迭代和广播。此外, PyArrayDescr_Type 是其实例描述数据的数据类型描述符类型。最后,有21个新的标量数组类型,它们是新的python scalar,对应于数组可用的每个基本数据类型。另外还有10种类型是占位符,它们允许数组标量适应实际的Python类型层次结构。

PyArray类型和PyArrayObject

PyTypeObject PyArray_Type

ndarray的python类型是 PyArray_Type . 在C语言中,每个ndarray都是指向 PyArrayObject 结构。此结构的ob_类型成员包含指向 PyArray_Type Type对象。

type PyArrayObject
type NPY_AO

这个 PyArrayObject C-structure包含数组所需的所有信息。ndarray(及其子类)的所有实例都将具有此结构。为了将来的兼容性,通常应该使用提供的宏访问这些结构成员。如果你需要一个较短的名字,那么你可以利用 NPY_AO (已弃用)定义为等同于 PyArrayObject . 不推荐直接访问结构字段。使用 PyArray_*(arr) 而不是形式。从numpy1.20开始,这个结构的大小不被认为是numpyabi的一部分(参见成员列表末尾的注释)。

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 dependend private members */
} PyArrayObject;
PyObject_HEAD

这是所有Python对象都需要的。它至少由一个引用计数成员组成( ob_refcnt )以及指向typeobject的指针( ob_type ). (如果Python是使用特殊选项编译的,那么也可能存在其他元素,有关详细信息,请参见Python源代码树中的Include/object.h)。obu type成员指向Python类型的对象。

char *data

可通过 PyArray_DATA ,此数据成员是指向数组第一个元素的指针。这个指针可以(而且通常应该)被重铸为数组的数据类型。

int nd

提供此数组的维数的整数。当nd为0时,该数组有时称为秩0数组。这样的数组具有未定义的维度和步长,无法访问。宏 PyArray_NDIM 定义在 ndarraytypes.h 指向此数据成员。 NPY_MAXDIMS 是任何数组的最大维度数。

npy_intp dimensions

一个整数数组,提供每个维度中的形状,长度为nd。 \geq 1。整数总是足够大,足以在平台上容纳指针,因此维度大小仅受内存限制。 PyArray_DIMS 是与此数据成员关联的宏。

npy_intp *strides

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

PyObject *base

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

  • 如果这个数组没有自己的内存,那么基点指向拥有它的Python对象(可能是另一个数组对象)

  • 如果此数组具有(已弃用) NPY_ARRAY_UPDATEIFCOPYNPY_ARRAY_WRITEBACKIFCOPY 标志集,则此数组是“行为不正常”数组的工作副本。

什么时候? PyArray_ResolveWritebackIfCopy 如果调用,则将使用此数组的内容更新基指向的数组。

PyArray_Descr *descr

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

int flags

由宏指向 PyArray_FLAGS ,此数据成员表示指示如何解释数据指向的内存的标志。可能的标志是 NPY_ARRAY_C_CONTIGUOUSNPY_ARRAY_F_CONTIGUOUSNPY_ARRAY_OWNDATANPY_ARRAY_ALIGNEDNPY_ARRAY_WRITEABLENPY_ARRAY_WRITEBACKIFCOPYNPY_ARRAY_UPDATEIFCOPY .

PyObject *weakreflist

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

注解

其他成员被认为是私有的和版本相关的。如果结构的大小对代码很重要,则必须特别小心。如果代码依赖于 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版本兼容的解决方案需要使用运行时计算偏移量和分配大小。

pyarraydescru类型和PyArray描述

PyTypeObject PyArrayDescr_Type

这个 PyArrayDescr_Type 是数据类型描述符对象的内置类型,用于描述如何解释组成数组的字节。静态定义21个 PyArray_Descr 用于内置数据类型的对象。当它们参与引用计数时,它们的引用计数不应达到零。还有一个用户定义的动态表 PyArray_Descr 也被维护的对象。一旦一个数据类型描述符对象被“注册”,它也不应该被释放。函数 PyArray_DescrFromType (…)可用于检索 PyArray_Descr 来自枚举类型号(内置或用户定义)的对象。

type PyArray_Descr

这个 PyArray_Descr 结构位于 PyArrayDescr_Type . 虽然这里描述它是为了完整性,但它应该被认为是内部的 NumPy 和操作通过 PyArrayDescr_*PyDataType* 函数和宏。此结构的大小可能会随着numpy版本的不同而变化。为确保兼容性:

  • 从不声明结构的非指针实例

  • 从不执行指针算术

  • 从不使用 sizof(PyArray_Descr)

其结构如下:

typedef struct {
    PyObject_HEAD
    PyTypeObject *typeobj;
    char kind;
    char type;
    char byteorder;
    char flags;
    int type_num;
    int elsize;
    int alignment;
    PyArray_ArrayDescr *subarray;
    PyObject *fields;
    PyObject *names;
    PyArray_ArrFuncs *f;
    PyObject *metadata;
    NpyAuxData *c_metadata;
    npy_hash_t hash;
} PyArray_Descr;
PyTypeObject *typeobj

指向类型对象的指针,该对象是此数组元素对应的python类型。对于内置类型,这将指向相应的数组标量。对于用户定义的类型,这应该指向用户定义的类型对象。此typeobject可以从数组标量继承,也可以不从数组标量继承。如果它不是从数组标量继承的,那么 NPY_USE_GETITEMNPY_USE_SETITEM 标志应设置在 flags 成员。

char kind

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

char type

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

char byteorder

表示字节顺序的字符:“>”(big endian)、“<”(little-endian)、“=”(native)、“”(uncertible,ignore)。所有内置数据类型都具有byteorder“=”。

char flags

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

NPY_ITEM_REFCOUNT

指示此数据类型的项必须被引用计数(使用 Py_INCREFPy_DECREF

NPY_ITEM_HASOBJECT

等同于 NPY_ITEM_REFCOUNT .

NPY_LIST_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_FLAGCHKD型NPY_ITEM_REFCOUNT

int type_num

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

int elsize

对于总是大小相同(如long)的数据类型,这将保留数据类型的大小。对于不同数组可以具有不同元素大小的灵活数据类型,此值应为0。

int alignment

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

PyArray_ArrayDescr *subarray

如果这不是 NULL ,则此数据类型描述符是另一个数据类型描述符的C样式连续数组。换句话说,这个描述符描述的每个元素实际上是一些其他基本描述符的数组。对于另一个数据类型描述符中的字段,这是最有用的数据类型描述符。字段成员应为 NULL 如果这不是 NULL (基本描述符的字段成员可以是非- NULL 然而)。

type PyArray_ArrayDescr
typedef struct {
    PyArray_Descr *base;
    PyObject *shape;
} PyArray_ArrayDescr;
PyArray_Descr *base

基类型的数据类型描述符对象。

PyObject *shape

作为python元组的子数组的形状(总是c样式连续)。

PyObject *fields

如果这不是空值,那么这个数据类型描述符具有由Python字典描述的字段,这些字段的键是名称(如果给定,还包括标题),其值是描述这些字段的元组。回想一下,数据类型描述符总是描述一组固定长度的字节。字段是该固定长度集合的命名子区域。字段由另一个数据类型描述符和字节偏移量组成的元组来描述。或者,元组可以包含通常是Python字符串的标题。这些元组放在这个字典中,按名称键控(如果给定的话还有标题)。

PyObject *names

字段名的有序元组。如果未定义字段,则为空。

PyArray_ArrFuncs *f

指向包含类型需要实现内部功能的函数的结构的指针。这些函数与后面描述的通用函数(UFUNC)不同。他们的签名可以随意更改。

PyObject *metadata

关于此元数据的数据类型。

NpyAuxData *c_metadata

特定于特定数据类型的C实现的元数据。为NumPy 1.7.0添加。

type npy_hash_t
npy_hash_t *hash

当前未使用。保留用于将来缓存哈希值。

type PyArray_ArrFuncs

实现内部功能的函数。并非所有这些函数指针都必须为给定类型定义。所需的成员是 nonzerocopyswapcopyswapnsetitemgetitemcast . 这些被认为是非- NULLNULL 条目将导致程序崩溃。其他功能可能是 NULL 这就意味着减少了该数据类型的功能。(此外,如果非零函数为 NULL 注册用户定义的数据类型时)。

typedef struct {
    PyArray_VectorUnaryFunc *cast[NPY_NTYPES];
    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;
    PyArray_FastClipFunc *fastclip;  /* deprecated */
    PyArray_FastPutmaskFunc *fastputmask;  /* deprecated */
    PyArray_FastTakeFunc *fasttake;  /* deprecated */
    PyArray_ArgFunc *argmin;
} PyArray_ArrFuncs;

函数指针的描述中使用了行为段的概念。行为段是一个对齐的段,并按数据类型的本机字节顺序排列。这个 nonzerocopyswapcopyswapngetitemsetitem 函数可以(并且必须)处理行为不正确的数组。其他函数需要行为内存段。

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

要从当前类型转换为所有其他内置类型的函数指针数组。每个函数都强制转换一个连续的、对齐的和不交换的缓冲区 from 指向一个连续的、对齐的、没有交换的缓冲区 to 要强制转换的项目数由 n 和参数 弗罗马尔托尔 被解释为灵活数组的pyarrayobjects以获取项大小信息。

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

指向从数组对象的单个元素返回标准python对象的函数的指针。 arr 指向 data . 此函数必须能够正确处理“行为不当”(未对齐和/或交换)数组。

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)

这些成员都是指向要从中复制数据的函数的指针 srcdestswap 如有指示。arr值仅用于灵活( NPY_STRINGNPY_UNICODENPY_VOID )数组(从 arr->descr->elsize )第二个函数复制一个值,而第一个函数用提供的步幅循环n个值。这些函数可以处理不当行为 src 数据。如果 src 为空,则不执行任何复制。如果 swap 为0,则不发生字节交换。假设 destsrc 不要重叠。如果它们重叠,则使用 memmove (…)先是后是 copyswap(n) 值为空的 src .

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

指向比较数组两个元素的函数的指针, arr 指向 d1d2 . 此函数需要行为(对齐和不交换)的数组。如果 * d1 > * d2 0,如果 * d1 == * d2 和-1如果 * d1 < * d2 . 数组对象 arr 用于检索灵活数组的项大小和字段信息。

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

指向函数的指针,该函数检索 n 元素在 arr 从指向的元素开始 data . 此函数要求内存段是连续的并具有行为。返回值始终为0。返回最大元素的索引 max_ind .

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

指向将两个数相乘的函数的指针 n -将长度序列加在一起,并将结果放入指向的元素中 op 属于 arr . 两个序列的开头由 ip1ip2 . 要到达每个序列中的下一个元素,需要跳到 is1is2 字节 ,分别。此函数需要行为(尽管不一定是连续的)内存。

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

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

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

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

npy_bool nonzero(void *data, void *arr)

指向函数的指针,如果 arr 指向 data 是非零。此函数可以处理行为错误的数组。

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

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

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

指向函数的指针,该函数填充一个连续的 buffer 给定的 length 使用单个标量 value 地址是谁的。最后一个参数是获取可变长度数组的itemsize所需的数组。

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

指向特定排序算法的函数指针数组。一个特殊的排序算法是用一个键(到目前为止 NPY_QUICKSORTNPY_HEAPSORTNPY_MERGESORT 定义。这些排序是在假定连续和对齐数据的情况下进行的。

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

指向此数据类型排序算法的函数指针数组。可使用与排序相同的排序算法。产生排序的索引返回到 result (必须用索引0初始化到 length-1 包括在内)。

PyObject *castdict

要么 NULL 或包含用户定义数据类型的低级强制转换函数的字典。每个函数都包装在 PyCapsule* 并由数据类型编号键入。

NPY_SCALARKIND scalarkind(PyArrayObject *arr)

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

int **cancastscalarkindto

要么 NULL 或数组 NPY_NSCALARKINDS 指针。这些指针应该是 NULL 或指向整数数组的指针(以 NPY_NOTYPE )指示可以安全地将指定类型的此数据类型的标量强制转换为的数据类型(这通常意味着不会丢失精度)。

int *cancastto

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

void fastclip(void *in, npy_intp n_in, void *min, void *max, void *out)

1.17 版后已移除: 使用此函数将在 np.clip . 数据类型必须使用 PyUFunc_RegisterLoopForDescr 将自定义循环附加到 np.core.umath.clipnp.minimumnp.maximum .

1.19 版后已移除: 设置此函数已弃用,应始终禁用 NULL ,如果设置,将忽略它。

一个读取 n_in 项目来自 in 并写入 out 读取值,如果它在 minmax 或相应的限制(如果在外部)。内存段必须是连续的且行为良好,并且 minmax 可能是 NULL ,但不是两者兼而有之。

void fastputmask(void *in, void *mask, npy_intp n_in, void *values, npy_intp nv)

1.19 版后已移除: 设置此函数已弃用,应始终禁用 NULL ,如果设置,将忽略它。

接受指针的函数 in 一组数组 n_in 项目,指针 mask 一组数组 n_in 布尔值和指针 vals 一组数组 nv 项目。项目来自 vals 被复制到 in 无论价值在哪里 mask 为非零,平铺 vals 如果需要,如果 nv < n_in . 所有数组都必须是连续的且行为良好。

void fasttake(void *dest, void *src, npy_intp *indarray, npy_intp nindarray, npy_intp n_outer, npy_intp m_middle, npy_intp nelem, NPY_CLIPMODE clipmode)

1.19 版后已移除: 设置此函数已弃用,应始终禁用 NULL ,如果设置,将忽略它。

接受指针的函数 src C连续的行为段,解释为三维形状数组 (n_outer, nindarray, nelem) 一个指针 indarraym_middle 整数索引和指针 dest C连续的行为段,解释为三维形状数组 (n_outer, m_middle, nelem) . 指数 indarray 用于索引 src 沿着第二维度,复制 nelem 项目纳入 dest . clipmode (可以接受值 NPY_RAISENPY_WRAPNPY_CLIP )确定小于0或大于0的索引 nindarray 将被处理。

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

指向函数的指针,该函数检索 n 元素在 arr 从指向的元素开始 data . 此函数要求内存段是连续的并具有行为。返回值始终为0。返回最小元素的索引 min_ind .

这个 PyArray_Type typeobject实现了 Python objects 包括 tp_as_numbertp_as_sequencetp_as_mappingtp_as_buffer 接口。这个 rich comparison )还与成员的新样式属性查找一起使用 (tp_members )和属性 (tp_getset )这个 PyArray_Type 也可以是子类型。

小技巧

这个 tp_as_number 方法使用泛型方法调用已注册用于处理该操作的任何函数。当 _multiarray_umath module 对于所有的ufs操作,它被导入到数值数组中。此选项可以更改为 PyUFunc_ReplaceLoopBySignature 这个 tp_strtp_repr 方法也可以使用 PyArray_SetStringFunction .

PyUFuncu类型和pyufunobject

PyTypeObject PyUFunc_Type

通过创建 PyUFunc_Type . 它是一个非常简单的类型,只实现基本的getattribute行为、打印行为,并且具有允许这些对象像函数一样工作的调用行为。UFUNC背后的基本思想是为支持该操作的每个数据类型保存对快速一维(矢量)循环的引用。这些一维循环都具有相同的签名,并且是创建新UFUNC的关键。它们由通用循环代码调用,以实现N维函数。还有一些为浮动和复杂浮动数组定义的通用一维循环,允许您使用单个标量函数定义UFUNC。( e.g. 阿坦纳)

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;
    PyUFunc_LegacyInnerLoopSelectionFunc *legacy_inner_loop_selector;
    PyUFunc_MaskedInnerLoopSelectionFunc *masked_inner_loop_selector;
    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;
} PyUFuncObject;
int nin

输入参数的数目。

int nout

输出参数的数目。

int nargs

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

int identity

要么 PyUFunc_OnePyUFunc_ZeroPyUFunc_MinusOnePyUFunc_NonePyUFunc_ReorderableNonePyUFunc_IdentityValue 以指示此操作的标识。它只用于对空数组进行类似reduce的调用。

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

一个函数指针数组---一个用于ufunc支持的每个数据类型。这是为实现基础函数而调用的向量循环 dims [0] 时代。第一个论点, args ,是一个数组 纳尔格斯 指向行为内存的指针。输入参数的数据指针是第一个,后面是输出参数的数据指针。必须跳过多少字节才能到达序列中的下一个元素由中的相应条目指定 步骤 数组。最后一个参数允许循环接收额外的信息。这是常用的,因此一个通用的向量循环可以用于多个函数。在这种情况下,要调用的实际标量函数作为 超文本 . 此函数指针数组的大小为类型。

void **data

要传递给一维向量循环的额外数据,或 NULL 如果不需要额外的数据。此C数组的大小必须相同( i.e. 键入)作为函数数组。 NULL 如果不需要额外的数据,则使用。几个对ufunc的C-API调用只是使用这些额外数据来接收指向要调用的实际函数的指针的一维向量循环。

int ntypes

UFUNC支持的数据类型数。这个数字指定有多少不同的一维循环(内置数据类型)可用。

int reserved1

未使用的

char *name

ufunc的字符串名称。它动态地用于构建 __doc__ UFUNC的属性。

char *types

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

char *doc

UFUNC文件。不应包含函数签名,因为当 __doc__ 检索。

void *ptr

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

PyObject *obj

对于从python函数动态创建的ufunc,此成员持有对底层python函数的引用。

PyObject *userloops

用户定义类型的用户定义的一维向量循环(存储为cobject ptrs)的字典。用户可以为任何用户定义的类型注册循环。它是按类型编号检索的。用户定义的类型号始终大于 NPY_USERDEF .

int core_enabled

标量UFunc为0;广义UFunc为1

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

一种函数,它解析类型并用输入和输出的数据类型填充数组。

PyUFunc_LegacyInnerLoopSelectionFunc *legacy_inner_loop_selector

返回内部循环的函数。这个 legacy 因为numpy 1.6已经计划了一个更好的变种。这个变种还没有出现。

void *reserved2

对于将来可能具有不同签名的循环选择器。

PyUFunc_MaskedInnerLoopSelectionFunc *masked_inner_loop_selector

函数,返回ufunc的屏蔽内部循环

npy_uint32 op_flags

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

npy_uint32 iter_flags

覆盖ufunc的默认nditer标志。

在API版本0x000000D中添加

npy_intp *core_dim_sizes

对于每个不同的核心维度,可能 frozen 大小IF UFUNC_CORE_DIM_SIZE_INFERRED0

npy_uint32 *core_dim_flags

对于每个不同的核心维度,一组 UFUNC_CORE_DIM* 旗帜

UFUNC_CORE_DIM_CAN_IGNORE

如果dim名称以 ?

UFUNC_CORE_DIM_SIZE_INFERRED

如果将从操作数而不是从 frozen 签名

PyObject *identity_value

还原标识,何时 PyUFuncObject.identity 等于 PyUFunc_IdentityValue .

PyArrayIter类型和PyArrayIterObject

PyTypeObject PyArrayIter_Type

这是一个迭代器对象,它使遍历N维数组变得容易。它是从ndarray的flat属性返回的对象。在整个实现内部,它也被广泛地用于在N维数组上循环。实现了tp_as_映射接口,这样迭代器对象就可以被索引(使用一维索引),并且通过tp_方法表实现了一些方法。这个对象实现了下一个方法,并且可以在迭代程序可以在Python中使用的任何地方使用。

type PyArrayIterObject

对应于对象的C结构 PyArrayIter_TypePyArrayIterObject . 这个 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];
    npy_intp  dims_m1[NPY_MAXDIMS];
    npy_intp  strides[NPY_MAXDIMS];
    npy_intp  backstrides[NPY_MAXDIMS];
    npy_intp  factors[NPY_MAXDIMS];
    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-d索引。它包含所需的尺寸产品。

PyArrayObject *ao

指向此迭代器创建用于表示的基础ndarray的指针。

char *dataptr

此成员指向由索引指示的ndarray中的元素。

npy_bool contiguous

如果基础数组是 NPY_ARRAY_C_CONTIGUOUS . 它用于尽可能简化计算。

如何在C级上使用数组迭代器将在后面的章节中更详细地解释。通常,您不需要关心迭代器对象的内部结构,只需要通过使用宏与它进行交互。 PyArray_ITER_NEXT (它) PyArray_ITER_GOTO (IT,DEST),或 PyArray_ITER_GOTO1D (IT,索引)。所有这些宏都需要参数 it 成为一个 PyArrayIterObject* .

PyArrayMultiIteru类型和PyArrayMultiIterObject

PyTypeObject PyArrayMultiIter_Type

这种类型提供了一个迭代器,它封装了广播的概念。它允许 N 要一起广播的数组,以便循环在广播的数组上以C样式的连续方式进行。相应的C结构是 PyArrayMultiIterObject 其内存布局必须以任何对象开头, obj ,传递给 PyArray_Broadcast (obj)功能。通过调整数组迭代器来执行广播,以便每个迭代器表示广播的形状和大小,但调整其步幅,以便在每次迭代中使用数组中的正确元素。

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

需要广播到同一形状的数组数。

npy_intp size

广播的总大小。

npy_intp index

广播结果中的当前(一维)索引。

int nd

广播结果中的维度数。

npy_intp *dimensions

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

PyArrayIterObject **iters

一个迭代器对象数组,用于保存迭代器以便将数组一起广播。返回时,迭代器被调整为广播。

PyArrayNeighborhoodIteru类型和PyArrayNeighborhoodIterObject

PyTypeObject PyArrayNeighborhoodIter_Type

这是一个迭代器对象,它使得在n维邻域上循环变得容易。

type PyArrayNeighborhoodIterObject

对应于对象的C结构 PyArrayNeighborhoodIter_TypePyArrayNeighborhoodIterObject .

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

PyArrayFlagsu类型和PyArrayFlagsObject

PyTypeObject PyArrayFlags_Type

从python中检索flags属性时,将构造此类型的特殊内置对象。这种特殊的类型通过将不同的标志作为属性访问,或者像对象是一个字典一样将标志名作为条目访问,使得使用不同的标志更加容易。

type PyArrayFlagsObject
typedef struct PyArrayFlagsObject {
        PyObject_HEAD
        PyObject *arr;
        int flags;
} PyArrayFlagsObject;

ScalarArrayTypes

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

BoolByteIntLongLongLongUByteUShortUIntULongULongLongHalf浮标双重的LongDoubleCFloatCDoubleCLongDouble统一码Void对象 .

这些类型名是C-API的一部分,因此可以在扩展C代码中创建。还有一个 PyIntpArrType_Type 和A PyUIntpArrType_Type 这是可以在平台上保存指针的整数类型之一的简单替换。这些标量对象的结构不向C代码公开。函数 PyArray_ScalarAsCtype (..)可用于从数组标量和函数中提取C类型值 PyArray_Scalar (…)可用于从C值构造数组标量。

其他C结构

一些新的C结构被发现在numpy的开发中是有用的。这些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中的缓冲区对象结构,直到ptr成员为止。在32位平台上( i.e. 如果 NPY_SIZEOF_INT = NPY_SIZEOF_INTP ,len成员还与缓冲区对象的等效成员匹配。表示通用的单段内存块很有用。

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

任何数据标志( e.g. NPY_ARRAY_WRITEABLE )这应该用来解释记忆。

PyArrayInterface

参见

数组接口

type PyArrayInterface

这个 PyArrayInterface 结构的定义使得numpy和其他扩展模块可以使用快速阵列接口协议。这个 __array_struct__ 支持快速数组接口协议的对象的方法应返回 PyCapsule 包含指向 PyArrayInterface 具有数组的相关详细信息的结构。创建新数组后,属性应为 DECREF D,它将释放 PyArrayInterface 结构。记住 INCREF 对象(其 __array_struct__ 属性)并指向新的 PyArrayObject 到同一个对象。这样就可以正确管理阵列的内存。

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

一种字符,根据typestring约定,用“t”->位字段、“b”->布尔值、“i”->有符号整数、“u”->无符号整数、“f”->浮点、“c”->复杂浮点、“o”->对象、“s”->(byte)字符串、“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_ALIGNEDNPY_ARRAY_C_CONTIGUOUSNPY_ARRAY_F_CONTIGUOUS 标志实际上可以从其他参数中确定。旗 NPY_ARR_HAS_DESCR (0x800)也可以设置为指示使用版本3数组接口的对象存在结构的DESCR成员(使用版本2数组接口的对象将忽略该成员)。

npy_intp *shape

包含每个维度中数组大小的数组。

npy_intp *strides

包含要跳转到每个维度中的下一个元素的字节数的数组。

void *data

指针 to 数组的第一个元素。

PyObject *descr

更详细地描述数据类型的python对象(与 德克思 键入 __array_interface__ )这可以 NULL 如果 类型项目尺寸 提供足够的信息。此字段也将被忽略,除非 NPY_ARR_HAS_DESCR 标志在 旗帜 .

内部使用的结构

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

type PyUFuncLoopObject

包含循环所需信息的C结构的松散包装。如果您试图理解ufunc循环代码,这很有用。这个 PyUFuncLoopObject 是相关的C结构。它在 ufuncobject.h 标题。

type PyUFuncReduceObject

一种C结构的松散包装物,其中包含了UFUNC的类还原方法所需的信息。如果您试图理解代码的reduce、accumulate和reduce,这是很有用的。这个 PyUFuncReduceObject 是相关的C结构。它在 ufuncobject.h 标题。

type PyUFunc_Loop1d

一个简单的C结构链接列表,其中包含为用户定义数据类型的每个已定义签名为UFUNC定义一维循环所需的信息。

PyTypeObject PyArrayMapIter_Type

高级索引是用这种python类型处理的。它只是一个围绕C结构的松散包装,其中包含高级数组索引所需的变量。相关的C-结构, PyArrayMapIterObject ,对于试图理解高级索引映射代码很有用。它在 arrayobject.h 标题。此类型不向Python公开,可以用C结构替换。作为一种python类型,它利用了引用计数内存管理。