本节使用一个简单的例子引出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. Ionic2+ 环境搭建

    ionic2+官方guide 基础环境安装 nodejs安装 ionic,cordova安装 npm install -g ionic cordova 项目创建 ionic start MyIonic ...

  2. ASP.NET Core 2.0 SignalR 示例

    # 一.前言 上次讲SignalR还是在<[在ASP.NET Core下使用SignalR技术](http://dotnet.ren/2017/02/21/%E5%9C%A8ASP-NET-Co ...

  3. PHP xmapp 下面安装 Composer-Setup.exe

    1.打开PHP配置文件E:\xampp\php\php.ini确认以下模块已开启(移除前面的分号). extension=php_openssl.dll, (php.ini文档里面开启一次就OK了) ...

  4. Java虚拟机面试重点-------------内存分配和回收策略

    1 对象优先分配在Eden区 对象优先在Eden进行分配,大多数情况下,对象在新生代Eden区进行分配.当Eden区没有足够的空间进行分配时,虚拟机会发起一次Minor GC. 新生代GC(Ninor ...

  5. maven仓库--搭建局域网私服(windows版)

    使用nexus搭建局域网私服 一. 认识maven仓库 1.1 maven仓库的作用   回想之前不用maven的时候,我们用eclipse原始的项目骨架构建项目时,在工程目录下往往有一个lib文件夹 ...

  6. JavaScript 的注释和快捷键

    添加必要的注释,对一个有责任心.有道德模范的前端必须具备的好习惯, 可以大大提高代码的可维护性.可读性. java代码注释快捷键:ctrl+shift+/首先熟悉一下html.css.js的注释的写法 ...

  7. vue.js实现内部自定义指令和全局自定义指令------directive

    在Vue中,我们平时数据驱动视图时候,内部自带的指令有时候解决不了一些需求,这时候,Vue给我们一个很好用的东东 directive 这个单词是我们写自定义指令的关键字哦 之定义指令为我们提供了几个钩 ...

  8. 构建自己的Tomcat镜像

    在很多情况下,我们会不满足于官方提供的Tomcat镜像.比如官方镜像的时区为UTC时间,并不是北京时间:再比如在特定硬件环境下,jdk的随机数生成器初始化过慢问题.此时,我们就会考虑构建自己的Tomc ...

  9. [CF337D]邪恶古籍-树状dp

    Problem 邪恶古籍 题目大意 给出一些关键点,求这棵树上到最远关键点距离小于等于d的有多少个. Solution 一个非常简单的树形dp.然而我被这道题给玩坏了. 在经过分析以后,我们发现只需要 ...

  10. 学习java应该了解一些html超文本标记语言(前端)

    在自己学习的过程中遇到一些内容,怕忘记所以借助博客加深印象也方便查找! html超文本标记语言中,分行级元素和块级元素. 行级元素的含义:行级元素不独占一行,相邻的行级元素在一行排列:行级元素可以控制 ...