numpy.i: a SWIG Interface File for NumPy

原文:https://docs.scipy.org/doc/numpy/reference/swig.interface-file.html

译者:飞龙 UsyiyiCN

校对:(虚位以待)

Introduction

简单包装和接口生成器(或SWIG)是一种用于生成用于连接到各种脚本语言的包装代码的强大工具。SWIG可以解析头文件,并仅使用代码原型,创建目标语言的接口。SWIG不是万能的。例如,它不能从原型知道:

double rms(double* seq, int n);

seq是什么。它是一个单一的值在现场改变?它是一个数组,如果是,它的长度是多少?它是仅输入的吗?仅输出?输入输出?SWIG无法确定这些详细信息,并且不尝试这样做。

If we designed rms, we probably made it a routine that takes an input-only array of length n of double values called seq and returns the root mean square. 但是,SWIG的默认行为是创建一个包装器函数,该函数编译,但是几乎不可能从脚本语言以C程序的方式使用。

对于Python,处理连续(或技术上,stride)同质数据块的首选方法是使用NumPy,它提供了对多维数据数据组的完全面向对象的访问。因此,rms函数的最逻辑的Python接口是(包括doc字符串):

def rms(seq):
    """
    rms: return the root mean square of a sequence
    rms(numpy.ndarray) -> double
    rms(list) -> double
    rms(tuple) -> double
    """

其中seq将是double值的NumPy数组,并且其长度n将在内部从seq被传递给C例程。更好的是,由于NumPy支持从任意Python序列构造数组,seq本身可以是一个几乎任意的序列(只要每个元素都可以转换为double)包装器代码将在提取其数据和长度之前将其内部转换为NumPy数组。

SWIG允许通过称为类型映射的机制定义这些类型的转换。本文档提供了有关如何使用numpy.i(一个SWIG界面文件)的信息,该界面文件定义了一系列旨在使上述类型的阵列相关转换相对简单的类型实施。例如,假设上面定义的rms函数原型位于名为rms.h的头文件中。要获取上述Python界面,您的SWIG界面文件需要以下内容:

%{
#define SWIG_FILE_WITH_INIT
#include "rms.h"
%}

%include "numpy.i"

%init %{
import_array();
%}

%apply (double* IN_ARRAY1, int DIM1) {(double* seq, int n)};
%include "rms.h"

类型映射由一个或多个函数参数的列表组成,按类型或按类型和名称。我们将引用这样的列表作为签名上面使用了由numpy.i定义的许多类型映射之一,并且具有签名(double * IN_ARRAY1, int DIM1)参数名称旨在表明double*参数是一个维度的输入数组,int表示该维度的大小。这正是rms原型中的模式。

最有可能的是,没有要包装的实际原型将具有参数名称IN_ARRAY1DIM1我们使用SWIG %apply指令将double类型的一维输入数组的typemap应用于rms因此,有效地使用numpy.i需要知道什么类型的电子书可用以及它们做什么。

包含上面给出的SWIG指令的SWIG接口文件将产生如下的包装代码:

 1 PyObject *_wrap_rms(PyObject *args) {
 2   PyObject *resultobj = 0;
 3   double *arg1 = (double *) 0 ;
 4   int arg2 ;
 5   double result;
 6   PyArrayObject *array1 = NULL ;
 7   int is_new_object1 = 0 ;
 8   PyObject * obj0 = 0 ;
 9
10   if (!PyArg_ParseTuple(args,(char *)"O:rms",&obj0)) SWIG_fail;
11   {
12     array1 = obj_to_array_contiguous_allow_conversion(
13                  obj0, NPY_DOUBLE, &is_new_object1);
14     npy_intp size[1] = {
15       -1
16     };
17     if (!array1 || !require_dimensions(array1, 1) ||
18         !require_size(array1, size, 1)) SWIG_fail;
19     arg1 = (double*) array1->data;
20     arg2 = (int) array1->dimensions[0];
21   }
22   result = (double)rms(arg1,arg2);
23   resultobj = SWIG_From_double((double)(result));
24   {
25     if (is_new_object1 && array1) Py_DECREF(array1);
26   }
27   return resultobj;
28 fail:
29   {
30     if (is_new_object1 && array1) Py_DECREF(array1);
31   }
32   return NULL;
33 }

来自numpy.i的typemaps负责以下代码行:12-20,25和30。第10行解析rms函数的输入。从格式字符串"O:rms",我们可以看到,参数列表应该是一个单独的Python对象(由冒号之前的O指定)指针存储在obj0中。调用由numpy.i提供的许多函数来进行和检查从通用Python对象到NumPy数组的(可能的)转换。这些函数在帮助函数部分解释,但希望他们的名字是不言自明的。在第12行,我们使用obj0构造一个NumPy数组。在第17行,我们检查结果的有效性:它是非空的,并且它具有任意长度的单维。一旦这些状态被验证,我们提取数据缓冲区和长度在行19和20,使我们可以调用底层C函数在第22行。第25行对我们创建了不再需要的新数组的情况执行内存管理。

此代码具有大量的错误处理。请注意,SWIG_failgoto fail的宏,引用第28行的标签。如果用户提供的参数数量不正确,将在第10行捕获。如果NumPy数组的构造失败或产生具有错误维数的数组,则这些错误在第17行被捕获。最后,如果检测到错误,则在线30仍然正确地管理存储器。

请注意,如果C函数签名的顺序不同:

double rms(int n, double* seq);

SWIG与上面给出的typemap签名与rms的参数列表不匹配。幸运的是,numpy.i有一组带有最后一个数据指针的类型:

%apply (int DIM1, double* IN_ARRAY1) {(int n, double* seq)};

这简单地具有在上面生成的代码的第3行和第4行中切换arg1arg2的定义的效果,以及它们在行19和20中的分配。

Using numpy.i

numpy.i文件目前位于numpy安装目录下的tools/swig子目录中。通常,你需要将它复制到你开发你的包装器的目录。

只使用单个SWIG接口文件的简单模块应包括以下内容:

%{
#define SWIG_FILE_WITH_INIT
%}
%include "numpy.i"
%init %{
import_array();
%}

在编译的Python模块中,import_array()应该只调用一次。这可能在您已经写入并链接到模块的C / C ++文件中。如果是这种情况,则您的所有接口文件都不应#define SWIG_FILE_WITH_INIT或调用import_array()或者,此初始化调用可以在由具有%init块的接口文件中由SWIG生成的包装文件中。如果是这种情况,并且您有多个SWIG接口文件,则只有一个接口文件应#define SWIG_FILE_WITH_INIT / t1>并调用import_array()

Available Typemaps

不同数据类型的数组(doubleint)的numpy.i提供的typemap指令以及不同类型的维度,例如intlong除了C和NumPy类型规范之外彼此相同。因此,通过宏来实现类型映射(通常在幕后):

%numpy_typemaps(DATA_TYPE, DATA_TYPECODE, DIM_TYPE)

可以针对适当的(DATA_TYPE, DATA_TYPECODE, DIM_TYPE)三元组调用。例如:

%numpy_typemaps(double, NPY_DOUBLE, int)
%numpy_typemaps(int,    NPY_INT   , int)

numpy.i接口文件使用%numpy_typemaps宏实现以下C数据类型和int维类型的类型映射:

  • 签名 char
  • 无符号 char
  • short
  • 无符号
  • int
  • 无符号 int
  • long
  • 无符号
  • long long
  • 无符号
  • float
  • double

在下面的描述中,我们引用一个通用的DATA_TYPE,它可以是上面列出的任何C数据类型和DIM_TYPE,它应该是许多类型的整数之一。

typemap签名在提供给缓冲区指针的名称上有很大区别。具有FARRAY的名称用于Fortran排序的数组,名为ARRAY的名称用于C序列(或1D数组)。

Input Arrays

输入数组被定义为传递到例程中但未在现场更改或返回给用户的数据的数组。因此,允许Python输入数组几乎是任何可以转换为所请求类型的数组的Python序列(例如列表)。输入数组签名为

1D:

  • DATA_TYPE IN_ARRAY1 [ANY]
  • DATA_TYPE * IN_ARRAY1, int DIM1
  • int DIM1, DATA_TYPE * IN_ARRAY1 / t6>

2D:

  • DATA_TYPE IN_ARRAY2 [ANY] [ANY]
  • DATA_TYPE * IN_ARRAY2, int DIM1, int DIM2
  • int DIM1, int DIM2, DATA_TYPE * IN_ARRAY2
  • DATA_TYPE * IN_FARRAY2, int DIM1, int DIM2
  • int DIM1, int DIM2, DATA_TYPE * IN_FARRAY2

3D:

  • DATA_TYPE IN_ARRAY3 [ANY] [ANY] [ANY]
  • DATA_TYPE * IN_ARRAY3, int DIM1, int DIM2, int DIM3
  • int DIM1, int DIM2, / t6> DIM3, DATA_TYPE * IN_ARRAY3
  • DATA_TYPE * IN_FARRAY3, int DIM1, int DIM2, int DIM3
  • int DIM1, int DIM2, / t6> DIM3, DATA_TYPE * IN_FARRAY3

4D:

  • (DATA_TYPE IN_ARRAY4 [ANY] [ANY] [ANY] [ANY])
  • (DATA_TYPE * IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4)
  • (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM_TYPE DIM4, DATA_TYPE * IN_ARRAY4) t0>
  • (DATA_TYPE * IN_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4)
  • (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3 , DIM_TYPE DIM4, DATA_TYPE * IN_FARRAY4)

列出的第一个签名, DATA_TYPE IN_ARRAY [ANY] 具有硬编码尺寸的二维数组。同样, DATA_TYPE IN_ARRAY2 [ANY] [ANY] 具有硬编码尺寸的三维数组,并且类似地用于三维。

In-Place Arrays

就地数组定义为就地修改的数组。输入值可以使用也可以不使用,但函数返回时的值是重要的。因此,提供的Python参数必须是必需类型的NumPy数组。就地签名是

1D:

  • DATA_TYPE INPLACE_ARRAY1 [ANY]
  • DATA_TYPE * INPLACE_ARRAY1, int DIM1
  • int DIM1, DATA_TYPE * INPLACE_ARRAY1 / t6>

2D:

  • DATA_TYPE INPLACE_ARRAY2 [ANY] [ANY]
  • DATA_TYPE * INPLACE_ARRAY2, int DIM1, int DIM2
  • int DIM1, int DIM2, DATA_TYPE * INPLACE_ARRAY2
  • DATA_TYPE * INPLACE_FARRAY2, int DIM1, int DIM2
  • int DIM1, int DIM2, DATA_TYPE * INPLACE_FARRAY2

3D:

  • DATA_TYPE INPLACE_ARRAY3 [ANY] [ANY] [ANY]
  • DATA_TYPE * INPLACE_ARRAY3, int DIM1, int DIM2, int DIM3
  • int DIM1, int DIM2, / t6> DIM3, DATA_TYPE * INPLACE_ARRAY3
  • DATA_TYPE * INPLACE_FARRAY3, int DIM1, int DIM2, int DIM3
  • int DIM1, int DIM2, / t6> DIM3, DATA_TYPE * INPLACE_FARRAY3

4D:

  • (DATA_TYPE INPLACE_ARRAY4 [ANY] [ANY] [ANY] [ANY])
  • (DATA_TYPE * INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4)
  • (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM_TYPE DIM4, DATA_TYPE * INPLACE_ARRAY4) t0>
  • (DATA_TYPE * INPLACE_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4)
  • (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3 , DIM_TYPE DIM4, DATA_TYPE * INPLACE_FARRAY4)

这些类型的映射现在检查以确保INPLACE_ARRAY参数使用本机字节顺序。如果不是,则引发异常。

对于您希望修改或处理每个元素的情况,还有一个“平面”就地数组,而不考虑维数。一个例子是“量化”函数,其将数组的每个元素就地量化,无论是1D,2D还是其它。此表单检查连续性,但允许C或Fortran排序。

ND:

  • (DATA_TYPE * INPLACE_ARRAY_FLAT, DIM_TYPE DIM_FLAT)

Argout Arrays

Argout数组是出现在C中的输入参数中的数组,但实际上是输出数组。当有多个输出变量并且单个返回参数不足时,通常会出现此模式。在Python中,返回多个参数的常规方法是将它们打包成一个序列(元组,列表等)并返回序列。这是argout typemaps做的。如果使用这些argout类型映射的包装函数具有多个返回参数,则根据Python的版本将它们打包到元组或列表中。Python用户不传递这些数组,它们只是简单地返回。对于指定维度的情况,python用户必须提供该维度作为参数。argout签名是

1D:

  • DATA_TYPE ARGOUT_ARRAY1 [ANY]
  • DATA_TYPE * ARGOUT_ARRAY1, int DIM1
  • int DIM1, DATA_TYPE * ARGOUT_ARRAY1 / t6>

2D:

  • DATA_TYPE ARGOUT_ARRAY2 [ANY] [ANY]

3D:

  • DATA_TYPE ARGOUT_ARRAY3 [ANY] [ANY] [ANY]

4D:

  • DATA_TYPE ARGOUT_ARRAY4 [ANY] [ANY] [ANY] [ANY] t0 >

这些通常用于在C / C ++中,您将在堆上分配一个(n)数组,并调用该函数来填充数组的情况。在Python中,数组被分配给你,并作为新的数组对象返回。

请注意,我们支持1D中的DATA_TYPE* argout类型映射,但不支持2D或3D。这是因为使用SWIG typemap语法的一个怪癖,并且无法避免。请注意,对于这些类型的1D类型映射,Python函数将使用表示DIM1的单个参数。

Argout View Arrays

Argoutview数组用于当你的C代码为你提供它的内部数据的视图,并且不需要用户分配任何内存。这可能是危险的。几乎没有办法保证C代码的内部数据在封装它的NumPy数组的整个生命周期内保持存在。如果用户在销毁NumPy数组之前破坏提供数据视图的对象,则使用该数组可能导致不良的内存引用或分段故障。然而,有些情况下,使用大数据集,你根本没有别的选择。

要为argoutview数组包装的C代码的特征在于指针:指向数据的维度和双指针的指针,以便这些值可以传递回用户。因此,argoutview类型的地图签名

1D:

  • DATA_TYPE ** ARGOUTVIEW_ARRAY1, DIM_TYPE * DIM1
  • DIM_TYPE * DIM1, DATA_TYPE ** ARGOUTVIEW_ARRAY1

2D:

  • DATA_TYPE ** ARGOUTVIEW_ARRAY2, DIM_TYPE * DIM1, DIM_TYPE * DIM2
  • ( DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2 )
  • DATA_TYPE ** ARGOUTVIEW_FARRAY2, DIM_TYPE * DIM1, DIM_TYPE * DIM2
  • ( DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_FARRAY2 )

3D:

  • DATA_TYPE ** ARGOUTVIEW_ARRAY3, DIM_TYPE * DIM1, DIM_TYPE * DIM2, DIM_TYPE * DIM3)
  • ( DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3)
  • DATA_TYPE ** ARGOUTVIEW_FARRAY3, DIM_TYPE * DIM1, DIM_TYPE * DIM2, DIM_TYPE * DIM3)
  • ( DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_FARRAY3)

4D:

  • (DATA_TYPE ** ARGOUTVIEW_ARRAY4, DIM_TYPE * DIM1, DIM_TYPE * DIM2, DIM_TYPE * DIM3, DIM_TYPE * DIM4)
  • (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEW_ARRAY4)
  • (DATA_TYPE ** ARGOUTVIEW_FARRAY4, DIM_TYPE * DIM1, DIM_TYPE * DIM2, DIM_TYPE * DIM3, DIM_TYPE * DIM4)
  • (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEW_FARRAY4)

请注意,不支持带有硬编码尺寸的数组。这些不能遵循这些类型映射的双指针签名。

Memory Managed Argout View Arrays

numpy.i的最近添加是允许argout数组将视图插入到受管内存中的类型映射。请参阅此处的讨论。

1D:

  • (DATA_TYPE ** ARGOUTVIEWM_ARRAY1, DIM_TYPE * DIM1)
  • (DIM_TYPE * DIM1, DATA_TYPE ** ARGOUTVIEWM_ARRAY1)

2D:

  • (DATA_TYPE ** ARGOUTVIEWM_ARRAY2, DIM_TYPE * DIM1, DIM_TYPE * DIM2)
  • (DIM_TYPE * DIM1, DIM_TYPE * DIM2, DATA_TYPE ** ARGOUTVIEWM_ARRAY2)
  • (DATA_TYPE ** ARGOUTVIEWM_FARRAY2, DIM_TYPE * DIM1, DIM_TYPE * DIM2)
  • (DIM_TYPE * DIM1, DIM_TYPE * DIM2, DATA_TYPE ** ARGOUTVIEWM_FARRAY2)

3D:

  • (DATA_TYPE ** ARGOUTVIEWM_ARRAY3, DIM_TYPE * DIM1, DIM_TYPE * DIM2, DIM_TYPE * DIM3)
  • (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEWM_ARRAY3)
  • (DATA_TYPE ** ARGOUTVIEWM_FARRAY3, DIM_TYPE * DIM1, DIM_TYPE * DIM2, DIM_TYPE * DIM3)
  • (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEWM_FARRAY3)

4D:

  • (DATA_TYPE ** ARGOUTVIEWM_ARRAY4, DIM_TYPE * DIM1, DIM_TYPE * DIM2, DIM_TYPE * DIM3, DIM_TYPE * DIM4)
  • (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_ARRAY4)
  • (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4)
  • (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_FARRAY4)

Output Arrays

numpy.i接口文件不支持输出数组的类型映射,原因有几个。首先,C / C ++返回参数被限制为单个值。这防止以通常的方式获得尺寸信息。第二,不允许具有硬编码长度的数组作为返回参数。换一种说法:

double[3] newVector(double x, double y, double z);

不是合法的C / C ++语法。因此,我们不能提供形式的typemaps:

%typemap(out) (TYPE[ANY]);

如果你遇到一个函数或方法返回一个指向数组的指针的情况,你最好的办法是写你自己的版本的函数要包装,或者用%extend类方法或%ignore%rename

Other Common Types: bool

请注意,可用类型图部分的列表不支持C ++类型boolNumPy bool是一个单字节,而C ++ bool是四个字节(至少在我的系统上)。因此:

%numpy_typemaps(bool, NPY_BOOL, int)

将导致会产生引用不正确数据长度的代码的typemaps。你可以实现以下宏扩展:

%numpy_typemaps(bool, NPY_UINT, int)

以修复数据长度问题,并且输入数组将正常工作,但就地数组可能会失败类型检查。

Other Common Types: complex

也不自动支持复杂浮点类型的字形图转换。这是因为Python和NumPy是用C编写的,它没有本机复杂类型。Python和NumPy对复杂变量实现了自己的(实质上等效的)struct定义:

/* Python */
typedef struct {double real; double imag;} Py_complex;

/* NumPy */
typedef struct {float  real, imag;} npy_cfloat;
typedef struct {double real, imag;} npy_cdouble;

我们可以实现:

%numpy_typemaps(Py_complex , NPY_CDOUBLE, int)
%numpy_typemaps(npy_cfloat , NPY_CFLOAT , int)
%numpy_typemaps(npy_cdouble, NPY_CDOUBLE, int)

这将为类型Py_complexnpy_cfloatnpy_cdouble的数组提供自动类型转换。但是,似乎不太可能有任何独立的(非Python,非NumPy)应用程序代码,人们将使用SWIG生成一个Python接口,也使用这些定义复杂类型。更有可能的是,这些应用程序代码将定义自己的复杂类型,或者在C ++的情况下,使用std::complex假设这些数据结构与Python和NumPy复杂类型兼容,则如上所示的%numpy_typemap扩展(用用户的复杂类型替换第一个参数)应该工作。

NumPy Array Scalars and SWIG

SWIG对数值类型进行了复杂的类型检查。例如,如果您的C / C ++例程需要一个整数作为输入,则由SWIG生成的代码将检查Python整数和Python长整数,如果提供的Python整数太大,则会引发溢出错误大到强制转换为C整数。随着NumPy标量数组到你的Python代码中的引入,你可能会想到从NumPy数组中提取一个整数,并试图将它传递给一个SWIG - 包装的C / C ++函数,期望一个int,但是SWIG类型检查不会将NumPy数组标量识别为整数。(通常,这实际上是工作 - 这取决于NumPy是否识别您正在使用的整数类型继承自您正在使用的平台上的Python整数类型。有时,这意味着在32位计算机上工作的代码在64位计算机上将失败。)

如果你得到一个Python错误,如下所示:

TypeError: in method 'MyClass_MyMethod', argument 2 of type 'int'

和你传递的参数是从NumPy数组提取的整数,那么你已经偶然发现了这个问题。解决方案是修改SWIG类型转换系统以接受NumPy数组标量以及标准整数类型。幸运的是,这个能力已经提供给你。只需复制文件:

pyfragments.swg

到你的项目的工作构建目录,这个问题将被修复。建议你这么做,这只是增加了你的Python接口的能力。

Why is There a Second File?

SWIG类型检查和转换系统是C宏,SWIG宏,SWIG类型映射和SWIG片段。片段是一种在需要时有条件地将代码插入到包装文件中的方法,如果不需要,则不插入它。如果多个类型图片需要相同的片段,片段只会插入一次您的包装代码。

有一个片段用于将Python整数转换为C long有一个不同的片段将Python整数转换为C int,调用在long片段中定义的rountine。我们可以通过更改long片段的定义来进行我们想要的更改。SWIG使用“先到先服务”系统确定片段的活动定义。也就是说,我们需要在SWIG之前定义long转换的片段。SWIG允许我们将片段定义放在文件pyfragments.swg中。如果我们将新的片段定义放在numpy.i中,它们将被忽略。

Helper Functions

numpy.i文件包含几个宏和例程,它们在内部使用它来构建其类型映射。但是,这些函数可能在您的接口文件中的其他位置有用。这些宏和例程被实现为片段,在上一节中简要描述。如果你尝试使用一个或多个下面的宏或函数,但是你的编译器抱怨它不能识别符号,那么你需要强制这些片段出现在代码中:

%fragment("NumPy_Fragments");

SWIG接口文件中。

Macros

is_array(a)
如果a是非NULL,可以转换为PyArrayObject*,则计算为true。
array_type(a)
评估a的整数数据类型代码,假设a可以转换为PyArrayObject*
array_numdims(a)
计算a的整数维数,假设a可以转换为PyArrayObject*
array_dimensions(a)
评估类型npy_intp和长度array_numdims(a)的数组,给出a的所有维度的长度,假设a可以转换为PyArrayObject*
array_size(a,i)
估计ai尺寸大小,假设a可以转换为PyArrayObject*
array_strides(a)
评估类型npy_intp和长度array_numdims(a)的数组,给出a的所有维度的stridess,假设a可以转换为PyArrayObject*步幅是沿着同一轴的元素与其直接相邻元素之间的距离(以字节为单位)。
array_stride(a,i)
评估ai步长,假设a可以转换为PyArrayObject*
array_data(a)
评估指向a的数据缓冲区的类型void*的指针,假设a可以转换为PyArrayObject*
array_descr(a)
返回对a的dtype属性(PyArray_Descr*)借用的引用,假设a可以转换为PyArrayObject*
array_flags(a)
返回表示a的标志的整数,假设a可以转换为PyArrayObject*
array_enableflags(a,f)
设置由af表示的标志,假设a可以转换为PyArrayObject*
array_is_contiguous(a)
如果a是连续数组,则计算为true。等效于(PyArray_ISCONTIGUOUS(a))
array_is_native(a)
如果a的数据缓冲区使用本机字节顺序,则计算为真。等同于(PyArray_ISNOTSWAPPED(a))
array_is_fortran(a)
如果a是FORTRAN有序,则计算为真。

Routines

pytype_string()

返回类型:const char *

参数:

  • PyObject * py_obj,一个通用的Python对象。

返回一个描述py_obj类型的字符串。

typecode_string()

返回类型:const char *

参数:

  • int 类型代码,NumPy整数类型代码。

返回一个描述与NumPy typecode对应的类型的字符串。

type_match()

返回类型:int

参数:

  • int actual_type,NumPy数组的NumPy类型代码。
  • int desired_type,所需的NumPy类型代码。

请确保actual_typedesired_type兼容。例如,这允许字符和字节类型,或int和long类型匹配。这现在等效于PyArray_EquivTypenums()

obj_to_array_no_conversion()

返回类型:PyArrayObject*

参数:

  • PyObject * 输入,一个通用的Python对象。
  • int 类型代码,所需的NumPy类型代码。

如果合法,则将input转换为PyArrayObject*,并确保其类型为typecode如果input无法转换,或typecode错误,请设置Python错误并返回NULL

obj_to_array_allow_conversion()

返回类型:PyArrayObject*

参数:

  • PyObject * 输入,一个通用的Python对象。
  • int 类型代码,结果数组的所需NumPy类型代码。
  • int * is_new_object,如果未执行转换,则返回值0,否则返回1。

使用给定的typecodeinput转换为NumPy数组。成功后,返回有效的PyArrayObject*,类型正确。失败时,Python错误字符串将被设置,并且例程返回NULL

make_contiguous()

返回类型:PyArrayObject*

参数:

  • PyArrayObject * ary,一个NumPy数组。
  • int * is_new_object,如果未执行转换,则返回值0,否则返回1。
  • int min_dims,允许的最小尺寸。
  • int max_dims,允许的最大尺寸。

检查ary是否连续。如果是这样,返回输入指针并将其标记为不是新对象。如果不是连续的,使用原始数据创建一个新的PyArrayObject*,将它标记为一个新对象并返回指针。

make_fortran()

返回类型:PyArrayObject*

参数

  • PyArrayObject * ary,一个NumPy数组。
  • int * is_new_object,如果未执行转换,则返回值0,否则返回1。

检查ary是否为Fortran连续。如果是这样,返回输入指针并将其标记为不是新对象。如果它不是Fortran连续的,使用原始数据创建一个新的PyArrayObject*,将其标记为一个新对象并返回指针。

obj_to_array_contiguous_allow_conversion()

返回类型:PyArrayObject*

参数:

  • PyObject * 输入,一个通用的Python对象。
  • int 类型代码,结果数组的所需NumPy类型代码。
  • int * is_new_object,如果未执行转换,则返回值0,否则返回1。

input转换为指定类型的连续PyArrayObject*如果输入对象不是连续的PyArrayObject*,那么将创建一个新对象并设置新的对象标志。

obj_to_array_fortran_allow_conversion()

返回类型:PyArrayObject*

参数:

  • PyObject * 输入,一个通用的Python对象。
  • int 类型代码,结果数组的所需NumPy类型代码。
  • int * is_new_object,如果未执行转换,则返回值0,否则返回1。

input转换为指定类型的Fortran连续PyArrayObject*如果输入对象不是Fortran连续的PyArrayObject*,那么将创建一个新对象并设置新的对象标志。

require_contiguous()

返回类型:int

参数:

  • PyArrayObject * ary,一个NumPy数组。

测试ary是否连续。如果是,返回1。否则,设置Python错误并返回0。

require_native()

返回类型:int

参数:

  • PyArray_Object * ary,一个NumPy数组。

要求ary不是字节交换。如果数组不是字节交换,则返回1。否则,设置Python错误并返回0。

require_dimensions()

返回类型:int

参数:

  • PyArrayObject * ary,一个NumPy数组。
  • int exact_dimensions,所需的维数。

要求ary具有指定数量的维度。如果数组具有指定的维数,则返回1。否则,设置Python错误并返回0。

require_dimensions_n()

返回类型:int

参数:

  • PyArrayObject * ary,一个NumPy数组。
  • int * exact_dimensions,表示可接受的维数的整数数组。
  • int n,长度为exact_dimensions

要求ary具有指定数量维度的列表之一。如果数组具有指定数目的维度之一,则返回1。否则,设置Python错误字符串并返回0。

require_size()

返回类型:int

参数:

  • PyArrayObject * ary,一个NumPy数组。
  • npy_int * size,表示每个维度所需长度的数组。
  • int n,长度size

要求ary具有指定的形状。如果数组具有指定的形状,则返回1。否则,设置Python错误字符串并返回0。

require_fortran()

返回类型:int

参数:

  • PyArrayObject * ary,一个NumPy数组。

要求给定的PyArrayObject是Fortran排序。如果PyArrayObject已经是Fortran排序,则不执行任何操作。否则,设置Fortran排序标志并重新计算步幅。

Beyond the Provided Typemaps

There are many C or C++ array/NumPy array situations not covered by a simple %include "numpy.i" and subsequent %apply directives.

A Common Example

考虑点积函数的合理原型:

double dot(int len, double* vec1, double* vec2);

我们想要的Python接口是:

def dot(vec1, vec2):
    """
    dot(PyObject,PyObject) -> double
    """

这里的问题是有一个维度参数和两个数组参数,并且我们的类型图是为适用于单个数组的维设置的(事实上,SWIG不提供将len与使用两个Python输入参数的vec2)。推荐的解决方案如下:

%apply (int DIM1, double* IN_ARRAY1) {(int len1, double* vec1),
                                      (int len2, double* vec2)}
%rename (dot) my_dot;
%exception my_dot {
    $action
    if (PyErr_Occurred()) SWIG_fail;
}
%inline %{
double my_dot(int len1, double* vec1, int len2, double* vec2) {
    if (len1 != len2) {
        PyErr_Format(PyExc_ValueError,
                     "Arrays of lengths (%d,%d) given",
                     len1, len2);
        return 0.0;
    }
    return dot(len1, vec1, vec2);
}
%}

If the header file that contains the prototype for double dot() also contains other prototypes that you want to wrap, so that you need to %include this header file, then you will also need a %ignore dot; directive, placed after the %rename and before the %include directives. 或者,如果所讨论的函数是类方法,除了%ignore,您将要使用%extend而不是%inline

错误处理注意事项:请注意,my_dot会传回double,但也可能会引发Python错误。当向量长度不匹配时,生成的wrapper函数将返回一个Python浮点值0.0。由于这不是NULL,Python解释器不会知道检查错误。为此,我们为my_dot添加上面的%exception指令以获取我们想要的行为(注意,$action展开为对my_dot的有效调用)。一般来说,你可能需要写一个SWIG宏来执行此任务。

Other Situations

还有其他包装情况,其中numpy.i在您遇到它们时可能会有帮助。

  • 在某些情况下,可以使用%numpy_typemaps宏为您自己的类型实现类型映射。有关示例,请参见其他常见类型:bool其他常见类型:复杂部分。另一种情况是,如果您的维度的类型不是int(例如:long):

    %numpy_typemaps(double, NPY_DOUBLE, long)
    
  • 你可以使用numpy.i中的代码来编写自己的类型映射。例如,如果你有一个五维数组作为函数参数,你可以将适当的四维类型映射剪切并粘贴到你的接口文件中。对第四维的修改将是微不足道的。

  • 有时,最好的方法是使用%extend指令为您的类定义新方法(或重载现有方法),它采用PyObject*被转换为PyArrayObject*),而不是指向缓冲区的指针。在这种情况下,numpy.i中的帮助程序可能非常有用。

  • 写类型电子书可能有点不直观。如果你有关于为NumPy编写SWIG类型映射的特定问题,numpy.i的开发人员会监视讨论讨论Swig -user邮件列表。

A Final Note

当你使用%apply指令时,通常需要使用numpy.i,它会保持有效,直到你告诉SWIG不应该。如果你要包装的函数或方法的参数具有通用名称,例如lengthvector,这些类型映射可能会在你不期望或想要的情况下应用。因此,在完成特定类型的映射之后添加%clear指令始终是个好主意:

%apply (double* IN_ARRAY1, int DIM1) {(double* vector, int length)}
%include "my_header.h"
%clear (double* vector, int length);

一般来说,你应该把这些typemap签名具体放在你想要的地方,然后在你完成后清除它们。

Summary

开箱即用的numpy.i提供了支持NumPy数组和C数组之间转换的类型映射:

  • 这可以是12种不同标量类型之一:签名 char无符号 / t5>shortunsigned shortint unsigned intlongunsigned t20>long longunsigned longfloatdouble
  • 这支持每种数据类型的74个不同的参数签名,包括:
    • 一维,二维,三维和四维数组。
    • 只输入,in-place,argout,argoutview和内存管理的argoutview行为。
    • 硬编码尺寸,数据缓冲区尺寸规范和尺寸 - 数据缓冲区规范。
    • 对于2D,3D和4D数组,C排序(“最后一维最快”)或Fortran排序(“第一维最快”)。

numpy.i接口文件还为包装程序开发人员提供了其他工具,包括:

  • 具有用于实现用户选择(1)C数据类型的74个参数签名的三个参数的SWIG宏(%numpy_typemaps),(2)NumPy数据类型他们匹配),和(3)维度类型。
  • 十四个C宏和十五个C函数,可用于编写专门的类型映射,扩展或内联函数来处理所提供的类型映射不包括的情况。请注意,在版本1.6之后的API的某些方面弃用之前和之后,宏和函数都被特别编码为与NumPy C / API无关,而与NumPy版本号无关。