本节使用一个简单的例子引出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模块系列(二)----一个简单的例子的更多相关文章

  1. 扩展Python模块系列(三)----参数解析与结果封装

    在上一节中,通过一个简单的例子介绍了C语言扩展Python内建模块的整体流程,从本节开始讲开始深入讨论一些细节问题,在细节讨论中从始至终都会涉及[引用计数]的问题.首先讨论C语言封装的Python函数 ...

  2. 扩展Python模块系列(一)----开发环境配置

    本系列将介绍如何用C/C++扩展Python模块,使用C语言编写Python模块,添加到Python中作为一个built-in模块.Python与C之间的交互目前有几种方案: 1. 原生的Python ...

  3. 扩展Python模块系列(四)----引用计数问题的处理

    承接上文,发现在使用Python C/C++ API扩展Python模块时,总要在各种各样的地方考虑到引用计数问题,稍不留神可能会导致扩展的模块存在内存泄漏.引用计数问题是C语言扩展Python模块最 ...

  4. 扩展Python模块系列(五)----异常和错误处理

    在上一节中,讨论了在用C语言扩展Python模块时,应该如何处理无处不在的引用计数问题.重点关注的是在实现一个C Python的函数时,对于一个PyObject对象,何时调用Py_INCREF和Py_ ...

  5. Python并发编程-线程-一个简单的例子

    from threading import Thread import time def func(n): #子线程完成的 time.sleep(1) print(n) #多线程示例 for i in ...

  6. 用一个简单的例子来理解python高阶函数

    ============================ 用一个简单的例子来理解python高阶函数 ============================ 最近在用mailx发送邮件, 写法大致如 ...

  7. ROS与Matlab系列:一个简单的运动控制

    ROS与Matlab系列:一个简单的运动控制 转自:http://blog.exbot.net/archives/2594 Matlab拥有强大的数据处理.可视化绘图能力以及众多成熟的算法函数,非常适 ...

  8. Python使用Redis实现一个简单作业调度系统

    Python使用Redis实现一个简单作业调度系统 概述 Redis作为内存数据库的一个典型代表,已经在非常多应用场景中被使用,这里仅就Redis的pub/sub功能来说说如何通过此功能来实现一个简单 ...

  9. Spring-Context之一:一个简单的例子

    很久之前就想系统的学习和掌握Spring框架,但是拖了很久都没有行动.现在趁着在外出差杂事不多,就花时间来由浅入深的研究下Spring框架.Spring框架这几年来已经发展成为一个巨无霸产品.从最初的 ...

随机推荐

  1. if和for的应用

    语句 顺序 结束加分号 分支 让程序根据条件不同执行不同的代码 if语句 if(条件){代码} if(条件){代码}else{代码} else if(条件){代码} if嵌套 switch...cas ...

  2. Android Shape Divider

    安卓框架提供了一种LinearLayout 内部布局元素分割线的实现,建立一个指定长宽的矩形Shape: <?xml version="1.0" encoding=" ...

  3. golang的并发不等于并行

    先 看下面一道面试题: func main() { runtime.GOMAXPROCS(1) wg := sync.WaitGroup{} wg.Add(20) for i := 0; i < ...

  4. JavaScript函数的各种调用模式

    函数是JavaScript世界里的第一公民,换句话来说,就是我们如果可以精通JavaScript函数的使用,那么对JavaScript的运用可以更游刃有余了.熟悉JavaScript的人应该都知道,同 ...

  5. BOM浏览器对象模型下面几个比较实用的方法

    location对象 location.href-- 返回或设置当前文档的URL location.search -- 返回URL中的查询字符串部分.例如 http://www.dreamdu.com ...

  6. linux 配置Apache 、PHP

    1. 安装 Apache 安装apache,首先要使用管理员权限,如果如法获取请联系您的管理员. centos: yum install httpd httpd-devel 2. 安装PHP 同样也需 ...

  7. zlib报“LNK2001:无法解析的外部符号”错误

    这个错误一般是由使用导出dll时未加载对应的lib文件导致的,但是工程在正确配置了lib文件的情况下仍然报这个错误,经查,是由于dll导入工程和dll导出工程的函数调用约定不一致导致的. 一.函数调用 ...

  8. Java中的二进制及基本的位运算

    Java中的二进制及基本的位运算 二进制是计算技术中广泛采用的一种数制.二进制数据是用0和1两个数码来表示的数.它的基数为2,进位规则是"逢二进一",借位规则是"借一当二 ...

  9. 使用ajax方法实现form表单的提交(附源码)

    写在前面的话 在使用form表单的时候,一旦点击提交触发submit事件,一般会使得页面跳转,页面间的跳转等行为的控制权往往在后端,后端会控制页面的跳转及数据传递,但是在某些时候不希望页面跳转,或者说 ...

  10. scroll事件实现监控滚动条并分页显示示例(zepto.js )

    今天做了一个类似于手机端京东首页的页面,效果图如下: 刷新页面的时候,标题栏(也就是搜索栏),背景是透明的,当我们往下滑的时候,可以改变标题栏的背景透明度(渐变效果): 当标题栏滑过轮播图后,透明度就 ...