Python的C/C++扩展

By phidoit@gmail.com

可扩展性是Python的一大特色,一方面,由于Python是解释执行的,这导致运行速度会比编译型语言慢,因此可以通过使用C/C++重写核心部分代码以解决性能上的瓶颈(程序90%的时间再运行10%的代码);另一方面,可以通过扩展,达到添加整合一些额外的功能以及保持专有源代码的目的。在本文接下来的部分中我们将讨论如何编写C/C++扩展代码,并使用它们的功能。
    我们要建立的是一个可以在Python内运行的C/C++模块,因此需要解决如何使C代码和Python代码能够进行交互以及数据共享。扩展是通过为C代码编写包装函数(类似适配器)实现双向交互和数据共享的。

一.一般的包装模式

每一个包装函数主要做三件事:
1.把输入的Python对象转换为C/C++对象;
2.调用C/C++函数;
3.转换C/C++函数处理的输出结果为Python对象,并返回;

先用一个简单样例描述模块大体的过程:

//wrap.cpp

//1.C代码

#include "Python.h"
int add(int arg1, int arg2)
{
    return arg1 + arg2;
}
//2.add的包装函数:

static PyObject* wrap_add(PyObject *self, PyObject *args)
{
    //把输入的Python对象转换为C/C++能识别的数据

int arg1, arg2;
    if(!PyArg_ParseTuple(args, "ii", &arg1, &arg2))
    return NULL;
    //调用C/C++函数,得到结果

int result = add(arg1,arg2);
    //把得到的结果包装成Python对象,并返回

return (PyObject*)Py_BuildValue("i", result);
}
//3.为模块添加PyMethodDef方法数组

static PyMethodDef wrap_methods[] ={
    {"add", wrap_add, METH_VARARGS},
    {NULL, NULL}
};
//4.增加模块初始化函数InitModule

PyMODINIT_FUNC initwrap (void)
{
    Py_InitModule("wrap ", wrap_methods);
}

把上面的代码编译,生成wrap.pyd。
   Visual Studio 2005编译方法参考:
    http://blog.csdn.net/solo_lxy/archive/2007/07/20/1700515.aspx
   启动控制台切换到相应的工程目录,即可测试生成的模块:
   
   (PS:似乎只有Release模式生成的模块才能正常运行)

相关说明
    每个包装函数都有如下形式:
          PyObject * wrap_function(PyObject *, PyObject * args)
    函数第一个参数,有特殊用途,通常选择忽略。第二个参数是一个PyTuple(PyObject的子类型,和Python中的Tuple对应),是调用时Python传入的参数。
    函数PyArg_ParseTuple把Python对象转换为C的数据类型,其声明如下:
         int PyArg_ParseTuple(PyObject* args, char* format, ...);
    参数args必须是一个tuple对象,包含传递过来的参数, format 参数必须是格式化字符串。剩余参数是各个变量的地址,类型要与格式化字符串对应。如:
         int arg1, arg2;
        PyArg_ParseTuple(args, "ii", &arg1, &arg2);
    函数Py_BuildValue可以说是PyArg_ParseTuple的逆过程,它把C的数据类型包装为Python对象。
         return (PyObject*)Py_BuildValue("i", result);
    把调用C函数的结果result包装为Python的int对象,并返回。
        static PyMethodDef wrap_methods[] ={
            {"add", wrap_add, METH_VARARGS},
            {NULL, NULL}
        };
    这个数组包含多个数组,其中的每个数组都包含了一个函数的信息,以便解释器能够导入并调用它们,最后一个NULL数组表示列表的结束。 METH_VARARGS常量表示参数以元组形式传入。
        PyMODINIT_FUNC initwrap (void)
        {
            Py_InitModule("wrap ", wrap_methods);
        }
    模块初始化函数void initModuleName(),这部分代码在模块被导入的时候被解释器调用。这样所有的包装就已经完成了。

二.C++类的包装


// Example.cpp

class Numbers
{
public:
    Numbers(int first, double second)
        : m_first( first), m_second(second){}
    double NumMemberMult(void){ return m_first*m_second;}
private:
    int m_first;
    double m_second;
};
static void PyDelNumbers(void *ptr)
{
    Numbers * oldnum = static_cast<Numbers *>(ptr);
    delete oldnum;
    return;
}
PyObject *Example_new_Numbers(PyObject *, PyObject* args)
{
    int arg1;
    double arg2;
    int ok = PyArg_ParseTuple(args,"id",&arg1,&arg2);
    if(!ok) return NULL;
   //动态创建一个新对象

Numbers *newnum = new Numbers(arg1, arg2);
   //把指针newnum包装成PyCObject对象并返回给解释器

return PyCObject_FromVoidPtr( newnum, PyDelNumbers);
}
PyObject * Example_Numbers_MemberMult(PyObject *, PyObject* args)
{
    PyObject *pynum = 0;
    int ok = PyArg_ParseTuple( args, "O", &pynum);
    if(!ok) return NULL;
   //把PyCObject转换为void指针

void * temp = PyCObject_AsVoidPtr(pynum);
   //把void指针转换为一个Numbers对象指针

Numbers * thisnum = static_cast<Numbers *>(temp);
    //调用函数

double result = thisnum->NumMemberMult();
    //返回结果

return Py_BuildValue("d",result);
}

static PyMethodDef Example_methods[] = {
    {"Numbers", Example_new_Numbers, METH_VARARGS},
    {"NumMemberMult", Example_Numbers_MemberMult, METH_VARARGS},
    {NULL, NULL}
};
PyMODINIT_FUNC initExample (void)
{
    Py_InitModule("Example", Example_methods);
}

C++类的包装和C函数的包装大同小异,因为对类的包装是对函数的包装,所以仍需要用Python代码对扩展模块进行包装,才可以像类一样的使用。

#example.py
from Example import *

class example(object):
    def __init__(self,arg1,arg2):
        self._base = Numbers(arg1,arg2)
    def MemberMult(self):
        return NumMemberMult(self._base)


   这样C++类的包装也完成了。

三.C/C++中创建Python list

static PyObject* Windy_dict(PyObject *self, PyObject *args)
{
    //创建列表
    PyObject *newlist = PyList_New(0);
    PyList_Append(newlist, PyString_FromString("first"));
    PyList_Append(newlist, PyString_FromString("second"));
    PyList_Append(newlist, PyString_FromString("third"));
    //返回给解释器
    return newlist;
}

创建其它Python对象也类似list的创建,返回给解释器的都是一个对象指针。C/C++对Python对象的解析差不多是创建时的逆过程。具体的对象模型及API可以查阅相关参考文档。
Python v2.6.2 documentation » Python/C API Reference Manual » Concrete Objects Layer

Python的C/C++扩展相关推荐

  1. Python:C语言扩展

    Python:C语言扩展 (2013-05-29 14:38:02) 转载▼ 标签: 杂谈 分类:Python   没有什么比对着书敲的代码仍然出错更令人沮丧了.<Python编程入门经典> ...

  2. python源程序文件的扩展名_python程序文件扩展名知识点详解

    python程序文件的扩展名称是什么 python程序的扩展名有.py..pyc..pyo和.pyd..py是源文件,.pyc是源文件编译后的文件,.pyo是源文件优化编译后的文件,.pyd是其他语言 ...

  3. python程序文件的扩展名称是什么_python程序文件的扩展名称是什么_Python教程,python,扩展名...

    python绘制正方形螺旋线_Python教程 python绘制正方形螺旋线的方法:1.使用import turtle as t导入绘图库,并设置别名为t:2.使用t.goto(x,y)方法以某一点为 ...

  4. python批量修改文件扩展名

    python批量修改文件扩展名录 前言 代码如下 前言 利用python将文件夹里的.txt文件修改为.tif文件. 代码如下 import os dir='/home/下载/'#文件所在目录 fil ...

  5. python脚本文件的扩展命是什么_一些文件的扩展名

    python文件扩展名是什么 与Python相关的只有.py..pyc..pyd这三个扩展名,下面是Python相关所有的扩展名文件以及对应的功能. .py - 常规脚本 .py3 - Python3 ...

  6. python模块文件的扩展名不一定是py_Python文件扩展名.py?.pyc?.pyd?

    与Python相关的文件扩展名实际不止.py..pyc..pyd这三种,但这三种是最常见的,比如以Houdini 16.5.268的Python模块包路径搜索一下有哪些扩展名 与Python相关的只有 ...

  7. Python相关的文件扩展名

    与Python相关的文件扩展名实际不止.py..pyc..pyd这三种,但这三种是最常见的,比如以Houdini 16.5.268的Python模块包路径搜索一下有哪些扩展名 与Python相关的只有 ...

  8. 基于python的智能家居系统_基于Python Django的可扩展智能家居系统

    基于 Python Django 的可扩展智能家居系统 龚 鸣,余杨志,邓宏涛 * [摘 要] 针对现阶段智能家居系统智能化迭代开发的需求,分析当前智能家居 系统主控的相关实现技术,提出了基于 Pyt ...

  9. python与php8-php8的扩展arginfo生成工具及工具初体验

    php8提供了非常方便的扩展函数或类参数信息的生成工具. 只需要维护一份xyz.stub.php,就可以使用工具生成 xyz_arginfo.h. 毫无疑问,这种方式,又降低了广大 phper 开发扩 ...

最新文章

  1. 06_一对一和一对多
  2. 分布式计算第四章 RMI
  3. Nginx 状态监控、缓存的两种机制(学习笔记十四)
  4. Hadoop源码分析16: IPC流程(11) 整体流程
  5. mysql 自动备份_如何将mysql备份自动存储到minio
  6. iOS 使用UILocalizedIndexedCollation实现区域索引标题(Section Indexed Title)即拼音排序...
  7. jvm莫名退出问题解决
  8. bzoj 1677: [Usaco2005 Jan]Sumsets 求和(DP)
  9. android开发酷欧天气,酷欧天气的开发
  10. weblogic 10.x 上开发restful服务
  11. freeswitch之G729转码操作配置详解
  12. python怎么下载panda包_pandas python下载
  13. 为什么很多互联网公司喜欢招应届毕业生?
  14. 深圳两所新大学,来了!
  15. 【编程题】【Scratch一级】2022.03 飞翔的小猫
  16. 正好在线炒股医美概念涨幅居前
  17. 格力成立子公司造芯片 亲自挂帅的董明珠能成功吗
  18. Java 8 新特性——实践篇
  19. 来自星星的宝贝,我要如何发现你?
  20. VirtualBox :不能分配USB设备到虚拟电脑

热门文章

  1. Patent Writing and cultivation of innovation thinking
  2. 2021的第二封拒信,来自斯坦福电气工程
  3. HASHSET不能预留容量问题
  4. jira+mysql配置
  5. php - Api 接口写法规范和要求
  6. cometD离线消息
  7. [转]互联网产品经理必上的九个资讯+分析类网站
  8. hdu 1874畅通工程续(基础Floyd)
  9. hdu 5277(最大团问题)
  10. nyoj 307(最短路变形)