为Python编写一个简单的C语言扩展模块
最近在看pytorh方面的东西,不得不承认现在这个东西比较火,有些小好奇,下载了代码发现其中计算部分基本都是C++写的,这真是要我对这个所谓Python语音编写的框架或者说是库感觉到一丢丢的小失落,细细看了一下其中主要的思想就是逻辑控制部分都是用Python写的,计算部分这是用C++语言为其编写的扩展模块,而这扩展模块接口这是用纯C语言编写的,不得不说Python和C++真是从C发展处理的,不好用的时候就调用C,然后就搞定了,言归正传,其思路是用C和SSE和CUDA做连接,说白了就是C扩展模块一部分是在CPU上做运算的,这部分是用C++和SSE(向量计算来操作的),另一部分是在GPU上用CUDA来操作的,由此我便在好奇之下对这C扩展有了一些小好奇,本文则是对此进行了一些阐述。
本文是在 Python Cookbook 第三版 15.2 编写简单C语言扩展模块的 基础上继续的。
目录结构如下:

其中,sample 文件夹如下:

其中的 sample.c sample.h 为标准C语言程序和头文件,具体如下:
/* sample.h */ extern int gcd(int x, int y);
extern int in_mandel(double x0, double y0, int n);
extern int divide(int a, int b, int *remainder);
/* sample.c */
#include <math.h> /* Compute the greatest common divisor */
int gcd(int x, int y) {
int g = y;
while (x > ) {
g = x;
x = y % x;
y = g;
}
return g;
} /* Test if (x0,y0) is in the Mandelbrot set or not */
int in_mandel(double x0, double y0, int n) {
double x=,y=,xtemp;
while (n > ) {
xtemp = x*x - y*y + x0;
y = *x*y + y0;
x = xtemp;
n -= ;
if (x*x + y*y > ) return ;
}
return ;
} /* Divide two numbers */
int divide(int a, int b, int *remainder) {
int quot = a / b;
*remainder = a % b;
return quot;
}
gcc -shared -fPIC sample.c -o libsample.so
可以把 这个标准C文件编译成标准动态库 ,即 libsample.so
该操作在本文中没有任何意义,只为了证明该C语言文件正确。
在上一级目录执行如下命令:
python3 setup.py build_ext --inplace



其中,sample.cpython-35m-x86_64-linux-gnu.so 就是编译好的动态链接库,也就是我们的扩展模块。
以上编译操作中的 setup.py 文件内容如下:
# setup.py
from distutils.core import setup, Extension setup(name="sample",
ext_modules=[
Extension("sample",
["sample/sample.c", "pysample.c"],
include_dirs = ['sample'],
)
]
)
其中,name 是指编译好以后的Python包名称,这里面我们并没有实际意义,因为我们编译好以后只要这个模块即.so文件。
["sample/sample.c", "pysample.c"] 是我们编写的C语言代码,其中sample.c 是我们编写的功能代码, pysample.c 则是负责 C语言与Python 之间的语言交互。
Extension("sample", 是模块名称, 即 .so 文件名称。该名称不能随意更改,必须与pysample.c 的定义相同,否则 Python中是无法识别出模块的内容的。
其实,给Python写C语言扩展重要的,或者说难度较大的不一定是功能代码,这里则是sample.c 代码, 而是负责在两个环境中做数值转换的代码,也就是 pysample.c , 该代码才是真正扩展所要解决的。
某种程度上 pysample.c 更像是接口, 就像 头文件对于标准c文件一样。
测试编译好以后的扩展是否可用:

#example.py import sample
print(sample.gcd(,))
print(sample.in_mandel(,,))
print(sample.in_mandel(2.0,1.0,))
print(sample.divide(,))
=========================================================================
以下则是核心代码,也就是接口代码或者说是 环境转换代码:
pysample.c
#include "Python.h"
#include "sample.h" /* int gcd(int, int) */
static PyObject *py_gcd(PyObject *self, PyObject *args) {
int x, y, result; if (!PyArg_ParseTuple(args,"ii", &x, &y)) {
return NULL;
}
result = gcd(x,y);
return Py_BuildValue("i", result);
} /* int in_mandel(double, double, int) */
static PyObject *py_in_mandel(PyObject *self, PyObject *args) {
double x0, y0;
int n;
int result; if (!PyArg_ParseTuple(args, "ddi", &x0, &y0, &n)) {
return NULL;
}
result = in_mandel(x0,y0,n);
return Py_BuildValue("i", result);
} /* int divide(int, int, int *) */
static PyObject *py_divide(PyObject *self, PyObject *args) {
int a, b, quotient, remainder;
if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
return NULL;
}
quotient = divide(a,b, &remainder);
return Py_BuildValue("(ii)", quotient, remainder);
} /* Module method table */
static PyMethodDef SampleMethods[] = {
{"gcd", py_gcd, METH_VARARGS, "Greatest common divisor"},
{"in_mandel", py_in_mandel, METH_VARARGS, "Mandelbrot test"},
{"divide", py_divide, METH_VARARGS, "Integer division"},
{ NULL, NULL, 0, NULL}
}; /* Module structure */
static struct PyModuleDef samplemodule = {
PyModuleDef_HEAD_INIT,
"sample", /* name of module */
"A sample module", /* Doc string (may be NULL) */
-1, /* Size of per-interpreter state or -1 */
SampleMethods /* Method table */
}; /* Module initialization function */
PyMODINIT_FUNC
PyInit_sample(void) {
return PyModule_Create(&samplemodule);
}
接口文件中:
/* Module initialization function */
PyMODINIT_FUNC
PyInit_sample(void) {
return PyModule_Create(&samplemodule);
}
是接口的初始化代码,也是接口代码中唯一的一个非STATIC函数,该函数非静态允许在Python类中调用,也就是 import sample 时候的操作。
返回类型 PyMODINIT_FUNC 说明返回的是 模块 的创建对象,也就是 import sample 中 的模块。
PyInit_sample 该函数名的前部分是固定不变的, PyInit_ 是固定格式, 后面跟着的 sample 则是模块名称。
PyModule_Create 是具体的 模块创建代码, 其中 的参数则是 模块结构的说明变量的 地址, 也就是本文的 samplemodule 变量。
/* Module structure */
static struct PyModuleDef samplemodule = {
PyModuleDef_HEAD_INIT,
"sample", /* name of module */
"A sample module", /* Doc string (may be NULL) */
-1, /* Size of per-interpreter state or -1 */
SampleMethods /* Method table */
};
其中, “sample” 是模块名称, “ A sample module” 是模块的文档, -1 表示该模块不能被多个Python解释器同时公用(也就是不能保证并发访问的安全性)。
重点的是 SampleMethods 这个是模块中方法的描述变量,也就是方法表。
模块的描述变量中 比较要人不理解的是这个变量 PyModuleDef_HEAD_INIT , 这个变量形式上来看应该是一个宏定义的变量,这个变量的存在好像并没有什么意义。
为了进一步了解 宏定义变量 PyModuleDef_HEAD_INIT 查询了以下资料:
https://docs.python.org/3/c-api/module.html#c.PyModuleDef.m_base
PyModuleDef-
The module definition struct, which holds all information needed to create a module object. There is usually only one statically initialized variable of this type for each module.
- PyModuleDef_Base
m_base -
Always initialize this member to
PyModuleDef_HEAD_INIT.
- PyModuleDef_Base
由此可以看出这个变量在Python的C 扩展中是固定不变的,并没有必要继续深究,固定如此就好。
不过好奇心使然又接着继续研究了以下,发现下面的资料:
http://blog.csdn.net/cleverwyq/article/details/12130577
已经编译好的代码附上:
https://files.cnblogs.com/files/devilmaycry812839668/writing_a_simple_c_extension_module.tar.gz
环境为 Ubuntu 16.04 x86_64
gcc5.0
python3.5
为Python编写一个简单的C语言扩展模块的更多相关文章
- 用Python编写一个简单的Http Server
用Python编写一个简单的Http Server Python内置了支持HTTP协议的模块,我们可以用来开发单机版功能较少的Web服务器.Python支持该功能的实现模块是BaseFTTPServe ...
- 用C语言编写一个简单的词法分析程序
问题描述: 用C或C++语言编写一个简单的词法分析程序,扫描C语言小子集的源程序,根据给定的词法规则,识别单词,填写相应的表.如果产生词法错误,则显示错误信息.位置,并试图从错误中恢复.简单的恢复方法 ...
- 用 C 语言编写一个简单的垃圾回收器
人们似乎觉得编写垃圾回收机制是非常难的,是一种仅仅有少数智者和Hans Boehm(et al)才干理解的高深魔法.我觉得编写垃圾回收最难的地方就是内存分配,这和阅读K&R所写的malloc例 ...
- 编写一个简单的C++程序
编写一个简单的C++程序 每个C++程序都包含一个或多个函数(function),其中一个必须命名为main.操作系统通过调用main来运行C++程序.下面是一个非常简单的main函数,它什么也不干, ...
- 用python实现一个简单的词云
对于在windows(Pycharm工具)里实现一个简单的词云还是经过了几步小挫折,跟大家分享下,如果遇到类似问题可以参考: 1. 导入wordcloud包时候报错,当然很明显没有安装此包. 2. 安 ...
- Java入门篇(一)——如何编写一个简单的Java程序
最近准备花费很长一段时间写一些关于Java的从入门到进阶再到项目开发的教程,希望对初学Java的朋友们有所帮助,更快的融入Java的学习之中. 主要内容包括JavaSE.JavaEE的基础知识以及如何 ...
- 手把手教你编写一个简单的PHP模块形态的后门
看到Freebuf 小编发表的用这个隐藏于PHP模块中的rootkit,就能持久接管服务器文章,很感兴趣,苦无作者没留下PoC,自己研究一番,有了此文 0×00. 引言 PHP是一个非常流行的web ...
- 用Python写一个简单的Web框架
一.概述 二.从demo_app开始 三.WSGI中的application 四.区分URL 五.重构 1.正则匹配URL 2.DRY 3.抽象出框架 六.参考 一.概述 在Python中,WSGI( ...
- 使用Java编写一个简单的Web的监控系统cpu利用率,cpu温度,总内存大小
原文:http://www.jb51.net/article/75002.htm 这篇文章主要介绍了使用Java编写一个简单的Web的监控系统的例子,并且将重要信息转为XML通过网页前端显示,非常之实 ...
随机推荐
- (转)《SSO CAS单点系列》之 15分钟让你了解SSO技术到底是个什么鬼!
Web应用系统的演化总是从简单到复杂,从单功能到多功能模块再到多子系统方向发展. .当前的大中型Web互联网应用基本都是多系统组成的应用群,由多个web系统协同为用户提供服务. 多系统应用群,必然意味 ...
- redis.conf配置文件说明
# Redis 配置文件 # 当配置中需要配置内存大小时,可以使用 1k, 5GB, 4M 等类似的格式,其转换方式如下(不区分大小写) # # 1k => 1000 bytes # 1kb = ...
- redhat 5的中文包安装
中文包文件名.在iso文件的/server/文件夹下fonts-chinese-3.02-9.6.el5.noarch.rpmfonts-ISO8859-2-75dpi-1.0-17.1.noarch ...
- Django小项目web聊天
WEBQQ的实现的几种方式 1.HTTP协议特点 首先这里要知道HTTP协议的特点:短链接.无状态! 在不考虑本地缓存的情况举例来说:咱们在连接博客园的时候,当tcp连接后,我会把我自己的http头发 ...
- 一次org.springframework.jdbc.BadSqlGrammarException ### Error querying database Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException问题排查过程
先说结论: 因为在表设计中有一个商品描述字段被设置为desc,但desc是mysql中的关键字,如select id,name,desc,price from product;这条sql语句在查询时的 ...
- P1136 迎接仪式
P1136 迎接仪式 $O(n^{2}k)$:$f[i][k]$表示到第$i$个字符为止,交换$k$次,得到的最多子串数 那么枚举位置$j$,状态可以从$f[j][k-1]+1$转移过来 $O(nk^ ...
- Sublime Text 3图标更改
Sublime Text 3图标更改 步骤: 1.下载ico图标 2.然后更改图标 注意:重点讲解下,如何将png文件转换为ico图标: 网络上单独找sublime text 3的ico图标比较不好找 ...
- 【日志】修改redis日志路径
redis默认不记录log文件,需要在Redis.conf文件,找到loglevel notice,在其后的logfile "",双引号中,写redis的路径"/redi ...
- luogu P1192 台阶问题
https://www.luogu.org/problem/show?pid=1192 登楼梯 肯定能想到 dp[i] = dp[i-1] + dp[i-2] + ...+ dp[i-k] 然后想到 ...
- JVM的异常处理
异常处理的两大组成要素:抛出异常和捕获异常.这两大要素共同实现程序控制流的非正常转移. 抛出异常分为:显式和隐式两种. 显式抛异常的主题是应用程序,它指的是在程序中使用 “throw” 关键字.手动 ...