本节使用一个简单的例子引出Python C/C++ API的详细使用方法。针对的是CPython的解释器。

目标:创建一个Python内建模块test,提供一个功能函数distance, 计算空间中两个点之间的距离。

可以在Python代码这样使用test模块:

  1. import test
  2. s = test.distance((0, 0, 0), (1, 1, 1))

先上代码:

[test.c]

  1. #include <Python.h>
  2. #include <math.h>
  3.  
  4. static PyObject* distance(PyObject* self, PyObject* args)
  5. {
  6. double x0, y0, z0, x1, y1, z1;
  7.  
  8. if (!PyArg_ParseTuple(args, "(ddd)(ddd)", &x0, &y0, &z0, &x1, &y1, &z1))
  9. {
  10. return NULL;
  11. }
  12. return PyFloat_FromDouble(sqrt((x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1) + (z0 - z1) * (z0 - z1)));
  13. }
  14.  
  15. static PyMethodDef cformula_methods[] =
  16. {
  17. { "distance", distance, METH_VARARGS, "Return the 2D Distance of 2 Points." },
  18. { NULL, NULL, , NULL },
  19. };
  20.  
  21. PyMODINIT_FUNC inittest(void)
  22. {
  23. Py_InitModule3("test", cformula_methods, "Common test Written in C.");
  24. }

[Source.cpp]

  1. #include <Python.h>
  2.  
  3. PyMODINIT_FUNC inittest();
  4.  
  5. int main()
  6. {
  7. //PyImport_AppendInittab("test", inittest);
  8. Py_Initialize();
  9. inittest();
  10. PyRun_SimpleString("import test");
  11. PyRun_SimpleString("s = test.distance((0, 0, 0), (1, 1, 1))");
  12. PyRun_SimpleString("print s");
  13. return ;
  14. }

编译运行后,结果如下:

如果希望将test模块打包为一个动态链接库,供其他Python程序使用,即打包为test.pyd,在其他Python程序中可以直接import test,就和正常的Python内建模块一样。

步骤如下:

1) 在test.c同级目录下,新建一个python文件setup.py

2)setup.py:

  1. from distutils.core import setup, Extension
  2. 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整型对象返回。

  1. static PyObject* distance(PyObject* self, PyObject* args)

函数一定要声明为static,将其限定在此文件范围;参数self是Python内部使用的,遵循范式即可;参数args是函数的参数列表,是一个tuple。

  1. PyArg_ParseTuple(args, "(ddd)(ddd)", &x0, &y0, &z0, &x1, &y1, &z1)

此函数将参数列表args解析为两个tuple, 每个tuple是三个double类型的元素。如果参数列表不符合"(ddd)(ddd)"这种形式,直接返回NULL。

  1. PyFloat_FromDouble

此函数将一个C原生的double,封装Python 的PyFloatObject。

3. 定义该模块对外提供的函数接口

  1. static PyMethodDef cformula_methods[]

在此数据结构中定义模块test对外提供的函数接口,第一个参数"distance"是Python内部记录的,就是test.distance调用时,python查找的函数名;第二个参数distance是函数具体实现,本质是一个函数指针;第三个参数是METH_VARARGS告诉Python此函数一个典型的PyCFunction,参数为两个PyOBject*,参数2使用PyArg_ParseTuple来解析;最后一个是函数说明。

4.定义模块初始化函数

  1. PyMODINIT_FUNC inittest(void)
  2. {
  3. Py_InitModule3("test", cformula_methods, "Common test Written in C.");
  4. }

函数inittest必须这样定义,如果模块名为example,那么模块初始化函数为initexample。宏定义PyMODINIT_FUNC定义如下:

  1. # if defined(__cplusplus)
  2. # define PyMODINIT_FUNC extern "C" void
  3. # else /* __cplusplus */
  4. # define PyMODINIT_FUNC void
  5. # 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. 【Android Developers Training】 95. 创建一个同步适配器

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  2. JAVA基础——方法笔记

    java方法_学习笔记 由于我本人对java方法已经比较熟悉了,java方法的定义和使用也比较简单,这里只列举了基于我自身容易搞错的地方,希望对大家的学习有帮助!! 方法的参数可以是基本数据类型,如 ...

  3. input标签在只允许输入数字的时候添加的代码

    oninput="this.value=this.value.replace(/\D/g, '')"

  4. JS弹出下载对话框以及实现常见文件类型的下载

    写在前面 JS要实现下载功能,一般都是这么几个过程:生成下载的URL,动态创建一个A标签,并将其href指向生成的URL,然后触发A标签的单击事件,这样就会弹出下载对话框,从而实现了一个下载的功能. ...

  5. 动态数组ArrayList的使用

    1.定义类 package com.realhope.rmeal.bean; /** * * @author Wucy * 菜谱类 */ public class Menu{ private Inte ...

  6. ansible学习之路

    ansible安装

  7. WPF WebBrowser Memory Leak 问题及临时解决方法

    首先介绍一下内存泄漏(Memory Leak)的概念,内存泄露是指程序中已动态分配的堆内存由于某种原因未释放或者无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果. 最近在使用W ...

  8. 多元线性回归模型的特征压缩:岭回归和Lasso回归

    多元线性回归模型中,如果所有特征一起上,容易造成过拟合使测试数据误差方差过大:因此减少不必要的特征,简化模型是减小方差的一个重要步骤.除了直接对特征筛选,来也可以进行特征压缩,减少某些不重要的特征系数 ...

  9. JAVA实用案例之水印开发

    写在最前面 上周零零碎碎花了一周的时间研究水印的开发,现在终于写了个入门级的Demo,做下笔记同时分享出来供大家参考. Demo是在我上次写的 JAVA实用案例之文件导入导出(POI方式) 框架基础上 ...

  10. css 子div自适应父div高度

    <div class="out"> <div class="a"></div> <div class="b& ...