扩展Python模块系列(二)----一个简单的例子
本节使用一个简单的例子引出Python C/C++ API的详细使用方法。针对的是CPython的解释器。
目标:创建一个Python内建模块test,提供一个功能函数distance, 计算空间中两个点之间的距离。
可以在Python代码这样使用test模块:
import test
s = test.distance((0, 0, 0), (1, 1, 1))
先上代码:
[test.c]
#include <Python.h>
#include <math.h> static PyObject* distance(PyObject* self, PyObject* args)
{
double x0, y0, z0, x1, y1, z1; if (!PyArg_ParseTuple(args, "(ddd)(ddd)", &x0, &y0, &z0, &x1, &y1, &z1))
{
return NULL;
}
return PyFloat_FromDouble(sqrt((x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1) + (z0 - z1) * (z0 - z1)));
} static PyMethodDef cformula_methods[] =
{
{ "distance", distance, METH_VARARGS, "Return the 2D Distance of 2 Points." },
{ NULL, NULL, , NULL },
}; PyMODINIT_FUNC inittest(void)
{
Py_InitModule3("test", cformula_methods, "Common test Written in C.");
}
[Source.cpp]
#include <Python.h> PyMODINIT_FUNC inittest(); int main()
{
//PyImport_AppendInittab("test", inittest);
Py_Initialize();
inittest();
PyRun_SimpleString("import test");
PyRun_SimpleString("s = test.distance((0, 0, 0), (1, 1, 1))");
PyRun_SimpleString("print s");
return ;
}
编译运行后,结果如下:

如果希望将test模块打包为一个动态链接库,供其他Python程序使用,即打包为test.pyd,在其他Python程序中可以直接import test,就和正常的Python内建模块一样。
步骤如下:
1) 在test.c同级目录下,新建一个python文件setup.py
2)setup.py:
from distutils.core import setup, Extension
setup(ext_modules=[Extension('test', sources = ['test.c'])])
3) 执行python命令: python setup.py build

这种情况是因为没有指定C的编译器,本文使用VS2015提供的编译器,所以首先执行:SET VS90COMNTOOLS=%VS140COMNTOOLS%

会在该目录下发现build/lib.win32-2.7中有一个test.pyd,这就是编译后的动态库,可以直接import

实现细节:
1. 任何扩展Python模块的C程序,一般只需要包含<Python.h>头文件即可,文档中这样描述:
All function, type and macro definitions needed to use the Python/C API are included in your code by the following line:
#include "Python.h"
在包含了Python.h之后,隐含地会自动包含C语言的标准头文件<stdio.h>, <string.h>, <errno.h>, <limits.h>, <assert.h> and <stdlib.h>
2. 本质上Python C API提供了对C函数的wrapper。假设有一个现成的C函数, int add(int a, int b), 想把它实现在Python的模块里,需要实现一个wrapper函数 static PyObject* addAB(PyObject* self, PyObject* args), 将args解析为两个整数a、b,然后调用add(a,b),将返回值打包为一个Python整型对象返回。
static PyObject* distance(PyObject* self, PyObject* args)
函数一定要声明为static,将其限定在此文件范围;参数self是Python内部使用的,遵循范式即可;参数args是函数的参数列表,是一个tuple。
PyArg_ParseTuple(args, "(ddd)(ddd)", &x0, &y0, &z0, &x1, &y1, &z1)
此函数将参数列表args解析为两个tuple, 每个tuple是三个double类型的元素。如果参数列表不符合"(ddd)(ddd)"这种形式,直接返回NULL。
PyFloat_FromDouble
此函数将一个C原生的double,封装Python 的PyFloatObject。
3. 定义该模块对外提供的函数接口
static PyMethodDef cformula_methods[]
在此数据结构中定义模块test对外提供的函数接口,第一个参数"distance"是Python内部记录的,就是test.distance调用时,python查找的函数名;第二个参数distance是函数具体实现,本质是一个函数指针;第三个参数是METH_VARARGS告诉Python此函数一个典型的PyCFunction,参数为两个PyOBject*,参数2使用PyArg_ParseTuple来解析;最后一个是函数说明。
4.定义模块初始化函数
PyMODINIT_FUNC inittest(void)
{
Py_InitModule3("test", cformula_methods, "Common test Written in C.");
}
函数inittest必须这样定义,如果模块名为example,那么模块初始化函数为initexample。宏定义PyMODINIT_FUNC定义如下:
# if defined(__cplusplus)
# define PyMODINIT_FUNC extern "C" void
# else /* __cplusplus */
# define PyMODINIT_FUNC void
# endif /* __cplusplus */
针对cpp的实现加了修饰extern "C"。
Py_InitModule3创建模块test, 当import test时,Python解释器会找到inittest创建的模块test。
5. 测试代码Source.cpp
在Python虚拟机环境初始化 Py_Initialize()之后,调用inittest(),则会创建新模块test。
如果希望在import test时才初始化模块test,那么在Py_Initialize()之前调用PyImport_AppendInittab("test", inittest); 然后再注释掉initest()。
PyImport_AppendInittab:Add a single module to the existing table of built-in modules.The new module can be imported by the namename, and uses the function initfunc as the initialization function called on the first attempted import. This should be called before Py_Initialize().
本节通过一个简单的例子来展示了如何用C语言扩展Python模块,并简单解释了用到的Python C/C++ API。在下一节中,将更加深入地了解Python C/C++ API,以及在写扩展程序时,会遇到的坑,比如:异常处理、引用计数等等。
扩展Python模块系列(二)----一个简单的例子的更多相关文章
- 扩展Python模块系列(三)----参数解析与结果封装
在上一节中,通过一个简单的例子介绍了C语言扩展Python内建模块的整体流程,从本节开始讲开始深入讨论一些细节问题,在细节讨论中从始至终都会涉及[引用计数]的问题.首先讨论C语言封装的Python函数 ...
- 扩展Python模块系列(一)----开发环境配置
本系列将介绍如何用C/C++扩展Python模块,使用C语言编写Python模块,添加到Python中作为一个built-in模块.Python与C之间的交互目前有几种方案: 1. 原生的Python ...
- 扩展Python模块系列(四)----引用计数问题的处理
承接上文,发现在使用Python C/C++ API扩展Python模块时,总要在各种各样的地方考虑到引用计数问题,稍不留神可能会导致扩展的模块存在内存泄漏.引用计数问题是C语言扩展Python模块最 ...
- 扩展Python模块系列(五)----异常和错误处理
在上一节中,讨论了在用C语言扩展Python模块时,应该如何处理无处不在的引用计数问题.重点关注的是在实现一个C Python的函数时,对于一个PyObject对象,何时调用Py_INCREF和Py_ ...
- Python并发编程-线程-一个简单的例子
from threading import Thread import time def func(n): #子线程完成的 time.sleep(1) print(n) #多线程示例 for i in ...
- 用一个简单的例子来理解python高阶函数
============================ 用一个简单的例子来理解python高阶函数 ============================ 最近在用mailx发送邮件, 写法大致如 ...
- ROS与Matlab系列:一个简单的运动控制
ROS与Matlab系列:一个简单的运动控制 转自:http://blog.exbot.net/archives/2594 Matlab拥有强大的数据处理.可视化绘图能力以及众多成熟的算法函数,非常适 ...
- Python使用Redis实现一个简单作业调度系统
Python使用Redis实现一个简单作业调度系统 概述 Redis作为内存数据库的一个典型代表,已经在非常多应用场景中被使用,这里仅就Redis的pub/sub功能来说说如何通过此功能来实现一个简单 ...
- Spring-Context之一:一个简单的例子
很久之前就想系统的学习和掌握Spring框架,但是拖了很久都没有行动.现在趁着在外出差杂事不多,就花时间来由浅入深的研究下Spring框架.Spring框架这几年来已经发展成为一个巨无霸产品.从最初的 ...
随机推荐
- Linux用户管理-中
添加用户组命令groupadd 提示:groupadd命令的使用非常简单,但在生产环境中使用的不多,因此,会简单应用即可. 与groupadd命令有关的文件有:/etc/group :用户组相关文件/ ...
- 【JS】jquery通知插件toastr
toastr是一款非常棒的基于jquery库的非阻塞通知提示插件,toastr可设定四种通知模式:成功,出错,警告,提示,而提示窗口的位置,动画效果都可以通过能数来设置,在官方站可以通过勾选参数来生成 ...
- 打包可执行的jar
#配置项目路径 *除程序文件以外,其他相关素材也可以打包进jar,但在内部访问时需以包名作为跟路径,如hello/xxx/yyy.zzz mkdir hello vi hello/HelloWorld ...
- usaco 2008 月赛 lites 开关灯 题解
题目: Farmer John尝试通过和奶牛们玩益智玩具来保持他的奶牛们思维敏捷. 其中一个大型玩具是 牛栏中的灯. N (2 <= N <= 100,000) 头奶牛中的每一 ...
- vijos1090题解
题目: 有n个正整数排成一行.你的目的是要从中取出一个或连续的若干个数,使它们的和能够被k整除. 例如,有6个正整数,它们依次为1.2.6.3.7.4.若k=3,则你可以取出1.2.6,或者2.6.3 ...
- ELK日志框架(2):log4net.ElasticSearch+ Kibana实现日志记录和显示
环境说明 1. windows server 2012 R2 64位 2. log4net.ElasticSearch 3. kibana-5.5.0-windows-x86.zip 架构说明 数据采 ...
- js如何获取样式?
在某个项目中,我们经常会需要来获取某个元素的样式,比如说获取一个div的color:这样,新的问出现了, var style = box.style.width;console.log(style); ...
- (cljs/run-at (JSVM. :all) "Metadata就这样哦")
前言 动态类型语言,少了静态类型语言必须声明变量类型的累赘,但也缺失了编译时类型检查和编译时优化的好处.cljs虽然作为动态类型语言,但其提供Metadata让我们在必要的时候可选择地补充类型提示, ...
- Python爬虫从入门到放弃(十八)之 Scrapy爬取所有知乎用户信息(上)
爬取的思路 首先我们应该找到一个账号,这个账号被关注的人和关注的人都相对比较多的,就是下图中金字塔顶端的人,然后通过爬取这个账号的信息后,再爬取他关注的人和被关注的人的账号信息,然后爬取被关注人的账号 ...
- 以Random Forests和AdaBoost为例介绍下bagging和boosting方法
我们学过决策树.朴素贝叶斯.SVM.K近邻等分类器算法,他们各有优缺点:自然的,我们可以将这些分类器组合起来成为一个性能更好的分类器,这种组合结果被称为 集成方法 (ensemble method)或 ...