使用C写Python的模块

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

1. 概述

Python 可以非常方便地和 C 进行相互的调用。

一般,我们不会使用 C 去直接编写一个 Python 的模块。通常的情景是,我们需要把 C 的相关模块包装一下,然后在 Python 中可以直接调用它。或者是,把 Python 逻辑中的某一效率要求很高的部分使用 C 来实现。整个过程大概是:

  1. 引入 Python.h 头文件。
  2. 编写包装函数。
  3. 函数中处理从 Python 传入的参数。
  4. 实现功能逻辑。
  5. 处理 C 中的返回值,包装成 Python 对象。
  6. 在一个 PyMethodDef 结构体中注册需要的函数。
  7. 在一个初始化方法中注册模块名。
  8. 把这个 C 源文件编译成链接库。
int add(int x, int y){
    return x + y;
}

//int main(void){
//    printf("%d", add(1, 2));
//    return 0;
//}

#include<Python.h>

static PyObject* W_add(PyObject* self, PyObject* args){
    int x;
    int y;
    if(!PyArg_ParseTuple(args, "i|i", &x, &y)){
        return NULL;
    } else {
        return Py_BuildValue("i", add(x, y));
    }
}

static PyMethodDef ExtendMethods[] = {
    {"add", W_add, METH_VARARGS, "a function from C"},
    {NULL, NULL, 0, NULL},
};

PyMODINIT_FUNC initdemo(){
    Py_InitModule("demo", ExtendMethods);
}

2. 引入 Python.h 头文件

这个文件一般位于 Python 的主目录中。比如我的 Ubuntu 10.04 下,它的位置在:

/usr/include/python2.6

在最后编译的时候指定目录就可以了。

3. 编写包装函数

因为 Python 用到的函数与普通的 C 函数,在输入和输出上,会有一些不同,所以,我们需要把普通的 C 做一些封来给 Python 用。

从另一方面来说,在实现功能的过程中,我们可以先完全不考虑这东西是拿给 Python 用的,只专注于使用 C 把它写好就可以了。最后,功能写好,测试没有问题之后,再做 Python 封装的工作。

包装函数一般声明成 static ,并且第一个参数是一个默认传入的 Python 对象,就是 Python 中某个对象的属性方法一样,第二个参数才是我们调用时传入的参数(实际上它是一个序列化后的字符串):

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

4. 处理从 Python 传入的参数

因为我们的相关函数,之后是在 Python 环境中被调用的,那么它显然接受的就是从 Python 环境下传入的参数。这和 C 中你看到的函数是不同的,在 Python 的世界中,一切都是对象。所以,包装函数中首先要处理的问题就是解析从 Python 占获取的参数。

常用的函数有: PyArg_ParseTuple

int x;
int y;
PyArg_ParseTuple(args, "i|i", &x, &y);

PyArg_ParseTuple 的作用是解析我们从 Python 中传入的 args 这个字符串,然后以我们规定的格式将解析结果放入指定变量的内存位。

i|i " 就表示要把传入的东西解析成两个整数,同样,还有 s 表示字符串等。

5. 实现逻辑功能

这部分没什么特别的,只需要在 C 中一样调用函数就可以了,相关变量我们已经在上一步处理过了。

add(x, y);

6. 处理 C 中的返回值

我们使用 C 完成了功能逻辑, C 中会产生一个返回值,要将这个值返回到我们之前调用函数的 Python 环境中,当然还需要经过一些处理才行。

常用的函数是: Py_BuildValue 。

return Py_BuildValue("i", add(x, y));

这个函数的用法和上一步中的 PyArg_ParseTuple 是一样的,它们过程相反。Py_BuildValue 把 C 中的值按给定的格式格式化成 Python 需要的对象。这里注意一下,对于W_add 这个函数,我们可是声明了它的返回类型为 PyObject// 的哦。

7. 注册函数

在上面的实现完成之后,就需要作导出的准备了。第一步,就是要在一个类型为 PyMethodDef的结构体中注册我们需要导出到 Python 中的函数:

static PyMethodDef ExtendMethods[] = {
    {"add", W_add, METH_VARARGS, "a function from C"},
    {NULL, NULL, 0, NULL},
}

这个结构体成员有四个函数:

  1. add " 导出后在 Pyhton 中可见的方法名。
  2. W_add 实际映射到 C 中的方法名。
  3. METH_VARARGS 表示传入方法的是普通参数,当然还可以处理关键词参数。
  4. 此方法的注释。

8. 注册模块

在注册了方法后,就要注册此模块了。方法是定义一个 init// 的函数:

PyMODINIT_FUNC initdemo(){
    Py_InitModule("demo", ExtendMethods);
}

方法名必须是 init 加上模块名,然后调用 Py_InitModule 来注册模块,这个函数的第一个参数就是模块名,第二个参数是此模块中我们导出的方法,就是上一步我们定义的结构体。

9. 编译

最后一步就是编译了。没什么特别的,指定好 Python.h 头文件的位置就可以了:

gcc demo.c -I /usr/include/python2.6 -shared -o demo.so

当然,链接库的名字要和我们期望导出的模块名一致。

这样,你就可以在 Python 中使用 import 直接引入 demo 模块,然后调用它的 add 方法了:

import demo
demo.add(3, 4)
目录
相关文章
|
15天前
|
测试技术 Python
手动解决Python模块和包依赖冲突的具体步骤是什么?
需要注意的是,手动解决依赖冲突可能需要一定的时间和经验,并且需要谨慎操作,避免引入新的问题。在实际操作中,还可以结合使用其他方法,如虚拟环境等,来更好地管理和解决依赖冲突😉。
|
25天前
|
Python
在Python中,可以使用内置的`re`模块来处理正则表达式
在Python中,可以使用内置的`re`模块来处理正则表达式
41 5
|
1月前
|
Java 程序员 开发者
Python的gc模块
Python的gc模块
|
1月前
|
数据采集 Web App开发 JavaScript
python-selenium模块详解!!!
Selenium 是一个强大的自动化测试工具,支持 Python 调用浏览器进行网页抓取。本文介绍了 Selenium 的安装、基本使用、元素定位、高级操作等内容。主要内容包括:发送请求、加载网页、元素定位、处理 Cookie、无头浏览器设置、页面等待、窗口和 iframe 切换等。通过示例代码帮助读者快速掌握 Selenium 的核心功能。
118 5
|
2月前
|
Python
SciPy 教程 之 SciPy 模块列表 6
SciPy教程之常量模块介绍:涵盖公制、二进制(字节)、质量、角度、时间、长度、压强、体积、速度、温度、能量、功率及力学单位。示例展示了角度单位转换为弧度的几个常用常量。
21 7
|
2月前
|
Python
SciPy 教程 之 SciPy 模块列表 7
`scipy.constants` 模块提供了常用的时间单位转换为秒数的功能。例如,`constants.hour` 返回 3600.0 秒,表示一小时的秒数。其他常用时间单位包括分钟、天、周、年和儒略年。
19 6
|
1月前
|
Python
SciPy 教程 之 SciPy 模块列表 13
SciPy教程之SciPy模块列表13:单位类型。常量模块包含多种单位,如公制、二进制(字节)、质量、角度、时间、长度、压强、体积、速度、温度、能量、功率和力学单位。示例代码展示了如何使用`constants`模块获取零摄氏度对应的开尔文值(273.15)和华氏度与摄氏度的转换系数(0.5556)。
18 1
|
1月前
|
XML 前端开发 数据格式
超级详细的python中bs4模块详解
Beautiful Soup 是一个用于从网页中抓取数据的 Python 库,提供了简单易用的函数来处理导航、搜索和修改分析树。支持多种解析器,如 Python 标准库中的 HTML 解析器和更强大的 lxml 解析器。通过简单的代码即可实现复杂的数据抓取任务。本文介绍了 Beautiful Soup 的安装、基本使用、对象类型、文档树遍历和搜索方法,以及 CSS 选择器的使用。
95 1
|
2月前
|
Python
SciPy 教程 之 SciPy 模块列表 9
SciPy教程之常量模块介绍,涵盖多种单位类型,如公制、质量、角度、时间、长度、压强等。示例展示了如何使用`scipy.constants`模块查询不同压强单位对应的帕斯卡值,包括atm、bar、torr、mmHg和psi。
16 1
|
2月前
|
Python
SciPy 教程 之 SciPy 模块列表 8
SciPy教程之常量模块单位类型介绍。该模块包含多种单位,如公制、质量、角度、时间、长度、压强、体积、速度、温度、能量、功率和力学单位。示例展示了部分长度单位的转换值,例如英寸、英尺、海里等。
17 1