-

使用任何编译语言(如C,C ++或Java)编写的任何代码都可以集成或导入到另一个Python脚本中。此代码被视为“扩展名”。

Python扩展模块只不过是一个普通的C库。在Unix机器上,这些库通常以.so(用于共享对象)结尾在Windows机器上,通常看到.dll(用于动态链接库)。

写作扩展的前提条件

要开始编写扩展名,你将需要Python头文件。

另外,假设你有很好的C或C ++知识,可以使用C编程来编写任何Python扩展。

首先看看一个Python扩展

您首先看看Python扩展模块,您需要将代码分为四部分 -

头文件Python.h

You need include Python.h header file in your C source file, which gives you access to the internal Python API used to hook your module into the interpreter.

Make sure to include Python.h before any other headers you might need. You need to follow the includes with the functions you want to call from Python.

The C Functions

The signatures of the C implementation of your functions always takes one of the following three forms −

static PyObject *MyFunction( PyObject *self, PyObject *args );

static PyObject *MyFunctionWithKeywords(PyObject *self,
                                 PyObject *args,
                                 PyObject *kw);

static PyObject *MyFunctionWithNoArgs( PyObject *self );

Each one of the preceding declarations returns a Python object. There is no such thing as a void function in Python as there is in C. If you do not want your functions to return a value, return the C equivalent of Python"s None value. The Python headers define a macro, Py_RETURN_NONE, that does this for us.

您的C函数的名称可以是任何您喜欢的,因为它们不会在扩展模块之外。它们被定义为静态函数。

您的C函数通常通过将Python模块和函数名组合在一起来命名,如下所示:

static PyObject *module_func(PyObject *self, PyObject *args) {
   /* Do your stuff here. */
   Py_RETURN_NONE;
}

这是模块模块内的一个名为func的Python函数您将把C函数的指针放入通常在源代码中接下来的模块的方法表中。

方法映射表

此方法表是PyMethodDef结构的简单数组。那个结构看起来像这样 -

struct PyMethodDef {
   char *ml_name;
   PyCFunction ml_meth;
   int ml_flags;
   char *ml_doc;
};

以下是此结构的成员的描述:

此表需要由适当成员的NULL和0值组成的哨兵终止。

对于上面定义的函数,我们有以下方法映射表 -

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, NULL },
   { NULL, NULL, 0, NULL }
};

初始化函数

扩展模块的最后一部分是初始化功能。当模块加载时,该Python函数由Python解释器调用。需要将该函数命名为init Module,其中Module模块的名称。

初始化函数需要从您将要创建的库导出。Python头文件定义了PyMODINIT_FUNC,以便包含恰当的咒语,以便针对我们正在编译的特定环境进行此操作。所有你需要做的是在定义功能时使用它。

您的C初始化函数通常具有以下整体结构 -

PyMODINIT_FUNC initModule() {
   Py_InitModule3(func, module_methods, "docstring...");
}

这里是Py_InitModule3函数的描述 -

把这一切放在一起看起来像以下 -

#include <Python.h>

static PyObject *module_func(PyObject *self, PyObject *args) {
   /* Do your stuff here. */
   Py_RETURN_NONE;
}

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, NULL },
   { NULL, NULL, 0, NULL }
};

PyMODINIT_FUNC initModule() {
   Py_InitModule3(func, module_methods, "docstring...");
}

利用上述所有概念的一个简单例子 -

#include <Python.h>

static PyObject* helloworld(PyObject* self)
{
    return Py_BuildValue("s", "Hello, Python extensions!!");
}

static char helloworld_docs[] =
    "helloworld( ): Any message you want to put here!!
";

static PyMethodDef helloworld_funcs[] = {
    {"helloworld", (PyCFunction)helloworld, 
     METH_NOARGS, helloworld_docs},
    {NULL}
};

void inithelloworld(void)
{
    Py_InitModule3("helloworld", helloworld_funcs,
                   "Extension module example!");
}

这里,Py_BuildValue函数用于创建Python值。将上述代码保存在hello.c文件中。我们将看到如何编译和安装这个模块,从Python脚本中调用。

创建和安装扩展:

distutils的包使得它很容易分发Python模块,无论是纯Python和扩展模块,以标准的方式。模块以源代码形式分发,并通过通常称为setup.py的安装脚本进行创建和安装,如下所示。

对于上述模块,您需要准备以下setup.py脚本 -

from distutils.core import setup, Extension
setup(name="helloworld", version="1.0",  
      ext_modules=[Extension("helloworld", ["hello.c"])])

现在,使用以下命令,它将执行所有需要的编译和链接步骤以及正确的编译器和链接器命令和标志,并将生成的动态库复制到相应的目录中 -

$ python setup.py install

在基于Unix的系统上,您很可能需要以root身份运行此命令,以便具有写入site-packages目录的权限。这通常在Windows上不是问题。

导入扩展

一旦你安装了你的扩展,你可以导入并在Python脚本中调用该扩展名如下:

#!/usr/bin/python
import helloworld

print helloworld.helloworld()

输出结果如下 -

Hello, Python extensions!!

传递函数参数

您最有可能希望定义接受参数的函数,您可以使用C函数的其他签名之一。例如,接下来的函数,接受一些参数,将被定义如下 -

static PyObject *module_func(PyObject *self, PyObject *args) {
   /* Parse args and do something interesting here. */
   Py_RETURN_NONE;
}

包含新函数的条目的方法表将如下所示:

static PyMethodDef module_methods[] = {
   { "func", (PyCFunction)module_func, METH_NOARGS, NULL },
   { "func", module_func, METH_VARARGS, NULL },
   { NULL, NULL, 0, NULL }
};

您可以使用API PyArg_ParseTuple函数从传递给C函数的一个PyObject指针中提取参数。

PyArg_ParseTuple的第一个参数是args参数。这是您将要解析的对象第二个参数是一个格式字符string,用于描述您希望显示参数的参数。每个参数由格式字符string中的一个或多个字符表示,如下所示。

static PyObject *module_func(PyObject *self, PyObject *args) {
   int i;
   double d;
   char *s;

   if (!PyArg_ParseTuple(args, "ids", &i, &d, &s)) {
      return NULL;
   }
   
   /* Do something interesting here. */
   Py_RETURN_NONE;
}

编译新版本的模块并导入它可以使用任意数量的任何类型的参数调用新函数 -

module.func(1, s="three", d=2.0)
module.func(i=1, d=2.0, s="three")
module.func(s="three", d=2.0, i=1)

你可以想出更多的变化。

PyArg_ParseTuple功能

这是PyArg_ParseTuple函数的标准签名-

int PyArg_ParseTuple(PyObject* tuple,char* format,...)

该函数为错误返回0,对于成功,该值不等于0。tuple是PyObject *,它是C函数的第二个参数。这里格式是一个C字符string,用于描述强制和可选参数。

以下是PyArg_ParseTuple函数的格式代码列表-

C型含义
Cchar 长度为1的Python字符string变为C字符string。
ddoublePython float变成C double。
FfloatPython float变成C float。
一世intPython int成为一个C int。
llongPython int变成C长。
L长久Python int变成长C长
OPyObject *获取非空借用对Python参数的引用。
schar *没有嵌入null的Python字符string到C char *。
s#char * + int任何Python字符string到C地址和长度。
t#char * + int只读单段缓冲区为C地址和长度。
Py_UNICODE *没有嵌入null的Python Unicode
你#Py_UNICODE * + int任何Python Unicode C地址和长度。
w#char * + int读/写单段缓冲区到C地址和长度。
zchar *像s一样,也接受None(将C char *设置为NULL)。
z#char * + int像s#一样,也接受None(将C char *设置为NULL)。
(...)按照 ...Python序列被视为每个项目的一个参数。
| 以下参数是可选的。
 格式化结束,后跟错误消息的函数名称。
; 格式化结束,后跟整个错误消息文本。

返回值

Py_BuildValue的格式字符string很像PyArg_ParseTuple您传递实际值的代替,而不是传递您正在创建的值的地址。下面是一个示例,显示如何实现一个添加功能 -

static PyObject *foo_add(PyObject *self, PyObject *args) {
   int a;
   int b;

   if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
      return NULL;
   }
   return Py_BuildValue("i", a + b);
}

这是如何在Python中实现的 -

def add(a, b):
   return (a + b)

您可以从函数返回两个值,如下所示,这将使用Python中的列表进行调用。

static PyObject *foo_add_subtract(PyObject *self, PyObject *args) {
   int a;
   int b;

   if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
      return NULL;
   }
   return Py_BuildValue("ii", a + b, a - b);
}

这是如何在Python中实现的 -

def add_subtract(a, b):
   return (a + b, a - b)

Py_BuildValue功能:

这是Py_BuildValue函数的标准签名-

PyObject* Py_BuildValue(char* format,...)

这里格式是一个描述要创建的Python对象的C字符string。下列参数Py_BuildValue来自哪个结果是建立C值。的PyObject *结果是一个新的参考。

下表列出了常用的代码字符string,其中零个或多个被连接成字符string格式。

C型含义
CcharAC char变为长度为1的Python字符string。
ddoubleAC double变成Python float。
FfloatAC float变成一个Python float。
一世intAC int成为Python int。
llongAC长时间成为Python int。
NPyObject *传递一个Python对象并窃取一个引用。
OPyObject *传递一个Python对象,并按照正常的方式INCREFs。
O&convert + void *任意转换
schar *C 0终止的char *到Python字符string,或NULL到无。
s#char * + intC char *和长度为Python字符string,或NULL为无。
Py_UNICODE *C-wide,以空字符结尾的Python Unicode字符string,或NULL为无。
你#Py_UNICODE * + intC字符string和长度为Python Unicode,或NULL为无。
w#char * + int读/写单段缓冲区到C地址和长度。
zchar *像s一样,也接受None(将C char *设置为NULL)。
z#char * + int像s#一样,也接受None(将C char *设置为NULL)。
(...)按照 ...从C值创建Python元组。
[...]按照 ...从C值创建Python列表。
{...}按照 ...从C值,交替键和值创建Python字典。

代码{...}从偶数C值,交替键和值创建字典。例如,Py_BuildValue(“{issi}”,23,“zig”,“zag”,42)返回一个类似Python的{23:"zig","zag":42}的字典。