📅  最后修改于: 2020-12-23 05:06:53             🧑  作者: Mango
您可以使用C,C+或Java等任何编译语言编写的任何代码都可以集成或导入到另一个Python脚本中。此代码被视为“扩展名”。
Python扩展模块只不过是一个普通的C库。在Unix机器上,这些库通常以.so结尾(对于共享对象)。在Windows计算机上,通常会看到.dll (用于动态链接库)。
要开始编写扩展程序,您将需要Python头文件。
在Unix机器上,这通常需要安装开发人员专用的软件包,例如 python2.5-dev 。
Windows用户在使用二进制Python安装程序时将这些标头作为软件包的一部分获得。
此外,假定您具有C或C++的良好知识。使用C编程编写任何Python扩展。
首先看一下Python扩展模块,您需要将代码分为四部分-
头文件Python.h 。
您想作为模块接口公开的C函数。
一张表,映射了您作为Python开发人员的函数名称,将其视为扩展模块中的C函数。
初始化函数。
您需要在C源文件中包含Python.h头文件,该文件使您可以访问内部Python API,该内部API将模块挂接到解释器中。
确保在可能需要的任何其他标头之前包含Python.h。您需要遵循包含要从Python调用的函数。
函数的C实现的签名始终采用以下三种形式之一-
static PyObject *MyFunction( PyObject *self, PyObject *args );
static PyObject *MyFunctionWithKeywords(PyObject *self, PyObject *args, PyObject *kw);
static PyObject *MyFunctionWithNoArgs( PyObject *self );
前面的每个声明都返回一个Python对象。 Python中没有像C那样的void函数。如果您不希望函数返回值,请返回C的Python None值。 Python标头定义了一个宏Py_RETURN_NONE,它可以为我们执行此操作。
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;
};
这是此结构的成员的描述-
ml_name-这是在Python程序中使用时, Python解释器显示的函数名称。
ml_meth-这是具有上一节中描述的任何签名的函数的地址。
ml_flags-这告诉解释器ml_meth正在使用三个签名中的哪个。
该标志的值通常为METH_VARARGS。
如果要允许关键字参数进入函数,可以将该标志与METH_KEYWORDS进行按位或运算。
它也可以具有METH_NOARGS的值,指示您不想接受任何参数。
ml_doc-这是该函数的文档字符串,如果您不想编写一个,则可以为NULL。
该表需要以一个由NULL和0值组成的适当成员的标记终止。
对于上面定义的函数,我们有以下方法映射表-
static PyMethodDef module_methods[] = {
{ "func", (PyCFunction)module_func, METH_NOARGS, NULL },
{ NULL, NULL, 0, NULL }
};
扩展模块的最后一部分是初始化函数。加载模块时, Python解释器将调用此函数。要求将函数命名为init Module ,其中Module是模块的名称。
需要从将要构建的库中导出初始化函数。 Python标头定义了PyMODINIT_FUNC,以包括针对我们正在编译的特定环境而发生的适当提示。您需要做的就是在定义函数时使用它。
您的C初始化函数通常具有以下总体结构-
PyMODINIT_FUNC initModule() {
Py_InitModule3(func, module_methods, "docstring...");
}
这是Py_InitModule3函数的描述-
func-这是要导出的函数。
module_methods-这是上面定义的映射表名称。
docstring-这是您要在扩展名中给出的注释。
放在一起,看起来像下面的样子:
#include
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
static PyObject* helloworld(PyObject* self)
{
return Py_BuildValue("s", "Hello, Python extensions!!");
}
static char helloworld_docs[] =
"helloworld( ): Any message you want to put here!!\n";
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 as的安装脚本来构建和安装。
对于上述模块,您需要准备以下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/python3
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参数。这是您将解析的对象。第二个参数是一个格式字符串,描述了您希望它们出现的参数。每个自变量由格式字符串中的一个或多个字符表示,如下所示。
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函数的标准签名-
int PyArg_ParseTuple(PyObject* tuple,char* format,...)
对于错误,此函数返回0,对于成功,返回不等于0的值。元组是PyObject *,它是C函数的第二个参数。这里的format是一个C字符串,描述了强制和可选参数。
这是PyArg_ParseTuple函数的格式代码列表-
Code | C type | Meaning |
---|---|---|
c | char | A Python string of length 1 becomes a C char. |
d | double | A Python float becomes a C double. |
f | float | A Python float becomes a C float. |
i | int | A Python int becomes a C int. |
l | long | A Python int becomes a C long. |
L | long long | A Python int becomes a C long long |
O | PyObject* | Gets non-NULL borrowed reference to Python argument. |
s | char* | Python string without embedded nulls to C char*. |
s# | char*+int | Any Python string to C address and length. |
t# | char*+int | Read-only single-segment buffer to C address and length. |
u | Py_UNICODE* | Python Unicode without embedded nulls to C. |
u# | Py_UNICODE*+int | Any Python Unicode C address and length. |
w# | char*+int | Read/write single-segment buffer to C address and length. |
z | char* | Like s, also accepts None (sets C char* to NULL). |
z# | char*+int | Like s#, also accepts None (sets C char* to NULL). |
(...) | as per ... | A Python sequence is treated as one argument per item. |
| | The following arguments are optional. | |
: | Format end, followed by function name for error messages. | |
; | Format end, followed by entire error message text. |
Py_BuildValue接受格式字符串,就像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函数的标准签名-
PyObject* Py_BuildValue(char* format,...)
这里的format是一个C字符串,描述了要构建的Python对象。 Py_BuildValue的以下参数是从中生成结果的C值。 PyObject *结果是一个新引用。
下表列出了常用的代码字符串,其中零个或多个以字符串格式连接。
Code | C type | Meaning |
---|---|---|
c | char | A C char becomes a Python string of length 1. |
d | double | A C double becomes a Python float. |
f | float | A C float becomes a Python float. |
i | int | A C int becomes a Python int. |
l | long | A C long becomes a Python int. |
N | PyObject* | Passes a Python object and steals a reference. |
O | PyObject* | Passes a Python object and INCREFs it as normal. |
O& | convert+void* | Arbitrary conversion |
s | char* | C 0-terminated char* to Python string, or NULL to None. |
s# | char*+int | C char* and length to Python string, or NULL to None. |
u | Py_UNICODE* | C-wide, null-terminated string to Python Unicode, or NULL to None. |
u# | Py_UNICODE*+int | C-wide string and length to Python Unicode, or NULL to None. |
w# | char*+int | Read/write single-segment buffer to C address and length. |
z | char* | Like s, also accepts None (sets C char* to NULL). |
z# | char*+int | Like s#, also accepts None (sets C char* to NULL). |
(...) | as per ... | Builds Python tuple from C values. |
[...] | as per ... | Builds Python list from C values. |
{...} | as per ... | Builds Python dictionary from C values, alternating keys and values. |
代码{...}从偶数个C值,交替的键和值构建字典。例如,Py_BuildValue(“ {issi}”,23,“ zig”,“ zag”,42)返回类似于Python的字典{23:'zig','zag':42}。