news 2026/4/23 2:10:29

Python - 扩展

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python - 扩展

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

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

扩展的前提条件

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

  • 在 Unix 机器上,这通常需要安装专门开发者的软件包。

  • Windows 用户在使用二进制 Python 安装程序时,会将这些头作为包的一部分获得。

此外,假设你有良好的C或C++知识,才能用C语言编程编写任何Python扩展。

首先看看 Python 扩展

在你第一次接触Python扩展模块时,你需要把代码分成四个部分——

  • 头文件Python.h

  • 你想从模块中暴露的C函数作为接口......

  • 一个映射函数名称的表,因为Python开发者在扩展模块中将它们视为C函数。

  • 一个初始化函数。

头文件 Python.h

你需要在 C 源文件中包含 Python.h 头文件,这样你就能访问用来将模块连接到解释器的内部 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 对象。Python 中没有像 C 那样的空函数。如果你不想让函数返回某个值,可以返回 Python 的None值的 C 版本。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的值,表明你不想接受任何论点。

  • mml_doc− 这是函数的docstring,如果不这样,它可能是NULL 想写一篇。

该表需要以一个由 NULL 和 0 值组成的哨兵来结束,适用于相应的成员。

示例

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

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

初始化函数

扩展模块的最后一部分是初始化函数。当模块加载时,Python 解释器调用了这个函数。函数必须命名为initModule,其中 Module 是模的名称。

初始化函数需要从你要构建的库导出。Python 头部定义了 PyMODINIT_FUNC,包含针对我们编译的特定环境所需的相应咒语。你只需要在定义函数时使用它。

你的C初始化函数通常具有以下总体结构——

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

以下是Py_InitModule3函数−的描述

  • func− 这是要导出的函数。

  • module_methods− 这是上述定义的映射表名称。

  • docstring− 这是你想在扩展中给出的评论。

综合这些,看起来如下 −

#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_func示例 一个利用上述所有概念的简单例子 − s, "Extension module example!"); }

这里使用Py_BuildValue函数构建 Python 值。把上面的代码保存在hello.c文件里。我们会学习如何编译并安装这个模块,以便用 Python 脚本调用。

建设与安装扩展部分

distutils包让以标准方式分发纯 Python 模块和扩展模块变得非常简单。模块以源代码形式分发,通过通常称为setup.pyas的设置脚本构建和安装。

对于上述模块,你需要准备以下 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脚本中的扩展,具体作如下 −

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 } };

你可以用APIPyArg_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函数

re 是PyArg_ParseTuple函数 − 的标准签名

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

该函数错误返回0,成功返回不等于0的值。元组是 C 函数的第二个参数 PyObject*。这里的格式是描述强制和可选参数的C字符串。

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

CodeC typeMeaning
ccharA Python string of length 1 becomes a C char.
ddoubleA Python float becomes a C double.
ffloatA Python float becomes a C float.
iintA Python int becomes a C int.
llongA Python int becomes a C long.
Llong longA Python int becomes a C long long.
OPyObject*Gets non-NULL borrowed reference to Python argument.
Schar*Python string without embedded nulls to C char*.
s#char*+intAny Python string to C address and length.
t#char*+intRead-only single-segment buffer to C address and length.
uPy_UNICODE*Python Unicode without embedded nulls to C.
u#Py_UNICODE*+intAny Python Unicode C address and length.
w#char*+intRead/write single-segment buffer to C address and length.
zchar*Like s, also accepts None (sets C char* to NULL).
z#char*+intLike 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非常相似的格式字符串。您传递的不是正在构建的值的地址,而是实际值。下面是一个示例,显示了如何实现add函数。

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 字符串。Py_BuildValue的以下参数是构建结果的C值。PyObject*结果是一个新的引用。

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

CodeC typeMeaning
ccharA C char becomes a Python string of length 1.
ddoubleA C double becomes a Python float.
ffloatA C float becomes a Python float.
iintC int becomes a Python int
llongA C long becomes a Python int
NPyObject*Passes a Python object and steals a reference.
OPyObject*Passes a Python object and INCREFs it as normal.
O&convert+void*Arbitrary conversion
schar*C 0-terminated char* to Python string, or NULL to None.
s#char*+intC char* and length to Python string, or NULL to None.
uPy_UNICODE*C-wide, null-terminated string to Python Unicode, or NULL to None.
u#Py_UNICODE*+intC-wide string and length to Python Unicode, or NULL to None.
w#char*+intRead/write single-segment buffer to C address and length.
zchar*Like s, also accepts None (sets C char* to NULL).
z#char*+intLike 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}这样的词典

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 4:56:33

高效安全的私有文档问答系统:Langchain-Chatchat深度解析

高效安全的私有文档问答系统&#xff1a;Langchain-Chatchat深度解析 在企业知识管理日益复杂的今天&#xff0c;一个常见的痛点浮出水面&#xff1a;技术手册、合同模板、内部制度等关键文档散落在各个角落&#xff0c;员工查找信息耗时费力&#xff0c;而一旦依赖公有云AI服…

作者头像 李华
网站建设 2026/4/22 7:10:02

对话百胜软件数据产品专家文斌丨数据炼油厂与AI超级顾问:DATAMAX如何让零售数据“活”起来

百闻不如一践&#xff0c;【百胜智见】为您解码百胜零售数智实践~本期导读&#xff1a;在数据爆炸的时代&#xff0c;零售企业坐拥“数据金山”却常常陷入“数据贫困”的困境。如何将分散、沉睡的数据转化为驱动业务增长的“活水”&#xff1f;百胜软件DATAMAX数据中台给出了智…

作者头像 李华
网站建设 2026/4/23 10:57:42

Quake 方言

Quake 方言总体介绍量子电路模型是应用最广泛的量子计算模型。它为表述量子算法提供了便利工具&#xff0c;也为量子计算机的物理构建提供了架构。量子电路将计算表示为一个应用于量子数据的量子算子序列。在我们的场景中&#xff0c;量子数据是一组量子比特。物理上&#xff0…

作者头像 李华
网站建设 2026/4/23 10:50:37

分割链表(dummy的用法)

思路很简单&#xff0c;将小于x的插入到small链表中&#xff0c;大于等于x的插入到large链表&#xff0c;最后将small插到large前面&#xff0c;返回small的头节点。但是插入的步骤很繁琐&#xff0c;需要设置头节点&#xff0c;甚至尾结点&#xff0c;在这里我们使用哨兵头节点…

作者头像 李华
网站建设 2026/4/23 10:57:44

8个AI论文工具,MBA毕业论文高效写作推荐!

8个AI论文工具&#xff0c;MBA毕业论文高效写作推荐&#xff01; AI 工具助力论文写作&#xff0c;高效又省心 在当前的学术环境中&#xff0c;MBA 学生面临着日益繁重的论文写作任务。从选题到开题、从撰写到降重&#xff0c;每一个环节都需要大量的时间和精力。而 AI 技术的兴…

作者头像 李华
网站建设 2026/4/23 10:57:36

Claude code免费体验+安装方式,对接国产大模型,Node + 配置教程

今天继续给大家介绍AI编程的环境搭建&#xff0c;使用IDE加上一个单独的client agent的这个模式。 在所有的这个agent里面&#xff0c;最好用的就是这个claude code。 Claude Code&#xff08;简称CC&#xff09;是目前最受欢迎的独立CLI工具之一&#xff0c;但由于账号申请和…

作者头像 李华