亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Industrial programming >> Python

Python - 使用 C 进行扩展编程

上一页下一页

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

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

编写扩展的先决条件

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

此外,假设您具备良好的 C 或 C++ 知识,可以使用 C 编程编写任何 Python 扩展。

先看一个 Python 扩展

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

头文件Python.h

您需要包含 Python.h C 源文件中的头文件,它使您可以访问用于将模块挂钩到解释器的内部 Python API。

确保在您可能需要的任何其他标头之前包含 Python.h。您需要遵循包含您要从 Python 调用的函数。

C 函数

函数的 C 实现的签名始终采用以下三种形式之一 -

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

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

static PyObject *MyFunctionWithNoArgs( PyObject *self );

前面的每个声明都返回一个 Python 对象。没有像 void 这样的东西 Python 中的函数与 C 中一样。如果您不希望函数返回值,请返回 Python 的 None 的 C 等效项 价值。 Python 头文件定义了一个宏 Py_RETURN_NONE,它为我们做这件事。

您的 C 函数的名称可以是您喜欢的任何名称,因为它们在扩展模块之外永远不会出现。它们被定义为 static 功能。

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

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

这是一个名为 func 的 Python 函数 模块 module 内部 .您将把指向 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 解释器调用。要求函数命名为 initModule , 其中 模块 是模块的名称。

初始化函数需要从您将要构建的库中导出。 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!!\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 package 使得以标准方式分发 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 身份运行此命令才能获得写入站点包目录的权限。这在 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 参数。这是您将要解析的对象 .第二个参数是一个格式字符串,描述您希望它们出现的参数。每个参数由格式字符串中的一个或多个字符表示,如下所示。

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 字符串。

这是 PyArg_ParseTuple 的格式代码列表 功能 -

代码 C 型 意义
c 字符 长度为 1 的 Python 字符串变成 C 字符。
d 一个 Python 浮点数变成一个 C 双精度数。
f 浮点数 Python 浮点数变成了 C 浮点数。
int Python int 变成 C int。
l Python int 变成 C long。
L 长长 Python int 变成 C long long
O PyObject* 获取对 Python 参数的非 NULL 借用引用。
s char* 没有嵌入空值到 C char* 的 Python 字符串。
s# char*+int 任何 Python 字符串到 C 地址和长度。
t# char*+int 只读单段缓冲区到 C 地址和长度。
u Py_UNICODE* 没有将空值嵌入到 C 中的 Python Unicode。
u# Py_UNICODE*+int 任何 Python Unicode C 地址和长度。
w# char*+int 读/写单段缓冲区到C地址和长度。
z char* 与 s 一样,也接受 None(将 C char* 设置为 NULL)。
z# char*+int 与 s# 一样,也接受 None(将 C char* 设置为 NULL)。
(...) 按照... Python 序列被视为每个项目一个参数。
| 以下参数是可选的。
: 格式结束,后跟错误消息的函数名。
; 格式结束,后跟整个错误消息文本。

返回值

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 功能

这是 Py_BuildValue 的标准签名 功能 -

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

这里格式 是一个 C 字符串,用于描述要构建的 Python 对象。 Py_BuildValue 的以下参数 是从中构建结果的 C 值。 PyObject* 结果是一个新的参考。

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

代码 C 型 意义
c 字符 一个 C 字符变成一个长度为 1 的 Python 字符串。
d 一个 C 的 double 变成一个 Python 的 float。
f 浮点数 一个 C 浮点数变成一个 Python 浮点数。
int 一个 C int 变成一个 Python int。
l C long 变成 Python int。
N PyObject* 传递一个 Python 对象并窃取一个引用。
O PyObject* 传递一个 Python 对象并像往常一样对其进行 INCREF。
O& convert+void* 任意转换
s char* C 0 终止的 char* 到 Python 字符串,或 NULL 到 None。
s# char*+int C char* 和长度为 Python 字符串,或 NULL 为 None。
u Py_UNICODE* C 范围的、以 null 结尾的字符串到 Python Unicode,或 NULL 到 None。
u# Py_UNICODE*+int C-wide 字符串和长度到 Python Unicode,或 NULL 到 None。
w# char*+int 读/写单段缓冲区到C地址和长度。
z char* 与 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} 的字典。


Python

  1. Python 面向对象编程
  2. Python Print() 语句:如何通过示例打印
  3. Python String strip() 函数与示例
  4. 带有示例的 Python 字符串计数()
  5. Python String format() 举例说明
  6. Python String find() 方法及示例
  7. 带有示例的 Python Lambda 函数
  8. 带有示例的 Python round() 函数
  9. 带有示例的 Python map() 函数
  10. Python Timeit() 与示例
  11. 集合中的 Python 计数器示例
  12. 带有示例的 Python 列表计数()