1  python扩展模块的组成

  在python中,对于一些和系统相关的模块或者对性能要求很高的模块,通常会把这个模块C化。扩展模块中主要包含下面几个部分:

  • init函数,函数名为:init+模块名,这个函数负责初始化模块,包括设置模块中的方法、对象和其它相关数据的初始化。这个函数是必须的,在脚本中第一次导入这个模块的时候,会先执行这个方法。
  • 定义模块方法描述表,它是一个static类型的PyMethodDef数据结构,用来描述模块中定义的方法。
  • C函数定义,这些函数是方法描述表中方法的具体实现。
  • 如果模块中定义了类,那么还要定义类方法描述表和对象类型。

2  实现python扩展模块实例

  当然,有了上面的组成部分,你还是不知道怎么实现一个模块,下面就用官方的一个例子来演示怎么实现一个python扩展模块,这个扩展模块用来实现在python中执行命令行命令。

 // spam.c
1 #include "Python.h" static PyObject *SpamError; static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts; if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
if (sts < ) {
PyErr_SetString(SpamError, "System command failed");
return NULL;
}
return PyLong_FromLong(sts);
} static PyMethodDef SpamMethods[] = {
{"system", spam_system, METH_VARARGS,
"Execute a shell command."},
{NULL, NULL, , NULL} /* Sentinel */
}; PyMODINIT_FUNC
initspam(void)
{
PyObject *m; m = Py_InitModule("spam", SpamMethods);
if (m == NULL)
return; SpamError = PyErr_NewException("spam.error", NULL, NULL);
Py_INCREF(SpamError);
PyModule_AddObject(m, "error", SpamError);
}

  上面的initspam是模块的初始化函数,函数开始调用了Py_InitModule初始化了一个名为spam的模块,模块的方法描述表是SpamMethods,它描述了模块有个名为system的方法,这个方法的c/c++实现是spam_system函数。从spam_system函数可以看到它就是调用system函数执行从python传过来的命令。有了上面的代码,我们怎样在python中使用了?很简单,先将上面代码编译成动态链接库,然后直接在python中用import语句导入这个模块就可以用了。在Windows下的用vs编译就行,不过在vs建立了dll工程后,需要设置下工程的属性,目的是设置python扩展涉及到的头文件路径和动态库。具体设置如下:先在VC++目录中设置include和lib路径,然后在链接器的附加依赖项中添加python27.lib库。

  设置好后直接编译就可以了,将编译生成的dll文件后缀名改成pyd,然后就可以在python中直接用import导入这个模块了。是不是非常的简单!!!!

3  实现python扩展模块中定义类

  上面的实现是在模块中定义函数来实现执行命令行命令,我们也可以在模块中定义类,然后用类的方法来执行这个命令。代码如下:

 // spam.c
1 #include "Python.h" static PyObject *SpamError; static PyObject *
spam_system(PyObject *self, PyObject *args)
{
const char *command;
int sts; if (!PyArg_ParseTuple(args, "s", &command))
return NULL;
sts = system(command);
if (sts < ) {
PyErr_SetString(SpamError, "System command failed");
return NULL;
}
return PyLong_FromLong(sts);
} static PyMethodDef SpamMethods[] = {
{"system", spam_system, METH_VARARGS,
"Execute a shell command."},
{NULL, NULL, , NULL} /* Sentinel */
}; PyTypeObject *SpamType = NULL; PyMODINIT_FUNC
initspam(void)
{
static PyTypeObject _SpamType = {
PyObject_HEAD_INIT(NULL)
, // ob_size
"spam.Spam", // tp_name
sizeof(PyObject), // tp_basicsize
, // tp_itemsize
, // tp_dealloc
, // tp_print
, // tp_getattr
, // tp_setattr
, // tp_compare
, // tp_repr
, // tp_as_number
, // tp_as_sequence
, // tp_as_mapping
, // tp_hash
, // tp_call
, // tp_str
, // tp_getattro
, // tp_setattro
, // tp_as_buffer
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE , // tp_flags
, // tp_doc
, // tp_traverse
, // tp_clear
, // tp_richcompare
, // tp_weaklistoffset
, // tp_iter
, // tp_iternext
SpamMethods, // tp_methods
, // tp_members
, // tp_getset
, // tp_base
, // tp_dict
, // tp_descr_get
, // tp_descr_set
, // tp_dictoffset
, // tp_init
, // tp_alloc
PyType_GenericNew, // tp_new
}; PyObject *m; m = Py_InitModule("spam", NULL);
if (m == NULL)
return;
if (PyType_Ready(&_SpamType) < )
return;
SpamType = &_SpamType;
Py_INCREF(SpamType);
PyModule_AddObject(m, "Spam", (PyObject*)SpamType);
SpamError = PyErr_NewException("spam.error", NULL, NULL);
Py_INCREF(SpamError);
PyModule_AddObject(m, "error", SpamError);
}

  上面的代码与之前的代码只是多了个Spam类的定义,使用的时候通过Spam的实例化对象来调用system函数。

4  Python/C API涉及的引用计数问题

  通过上面的例子,是不是觉得写python的C扩展模块非常的简单呢?其实不然,主要是python中有个引用计数问题,在写扩展模块的时候必须非常小心的处理,否则很有容易导致内存泄露。根据python官方的定义,在Python/C API中,引用计数的行为被归纳为三种:new reference、borrow reference和steal reference,前两种用于描述返回PyObject*类型的函数对返回的这个对象的引用计数的行为;后一种用于将一个PyObject*类型传入函数后,函数对这个对象的引用计数的行为。new referenc表示函数将这个对象引用的所有权转交给函数调用者了,由函数的调用者来管理这个引进的计数,也就是说调用者不用这个引用的时候必须显示的调用 Py_DECREF()或者Py_XDECREF()来释放这个引用,典型的函数是PyObject_、PyNumber_PySequence_和PyMapping_;borrow reference与new reference刚好相反,表示函数的调用者只管用这个引用,不用关心它的引用计数,用完了也不用显示调用Py_DECREF()或者Py_XDECREF()来释放这个引用,典型的函数是PyList_GetItem、PyTuple_GetItem;steal reference表示函数内部只会使用这个引用,不会调用Py_INCREF来增加这个引用的引用计数,相当于“偷了”被调用者的一个引用计数,典型的函数是PyList_SetItem()和PyTuple_SetItem()。因此,在编写C扩展的时,如果遇到某个Python/C API不确定是哪种reference的时候,建议查下官方文档,文档中会明确的说明这个函数是哪类reference(如下图所示),这样能大大减少引用计数的问题。

5  参考

  python官网

快速实现python c扩展模块的更多相关文章

  1. java程序员快速掌握python系列——概述

    这一系列主要是总结学习python过程中的方方面面(已经学完,时间大概是一周左右).当然限于个人水平java也就是够用,python短时间内也不可能深入到哪里去.所以这次的分享的目的是能够快速使用py ...

  2. 快速查询Python脚本语法

    /********************************************************************* * 快速查询Python脚本语法 * 说明: * Char ...

  3. 程序员带你十天快速入门Python,玩转电脑软件开发(四)

    本系列文章立志于从一个已经习得一门编程语言的基础之上,全面介绍Python的相关开发过程和相关经验总结.本篇文章主要是基于上一篇的程序员带你十天快速入门Python,玩转电脑软件开发(三)的基础之上, ...

  4. 程序员带你十天快速入门Python,玩转电脑软件开发(三)

    声明:本次教程主要适用于已经习得一门编程语言的程序员.想要学习第二门语言.有梦想,立志做全栈攻城狮的你 . 如果是小白,也可以学习本教程.不过可能有些困难.如有问题在文章下方进行讨论.或者添加QQ群5 ...

  5. 程序员带你十天快速入门Python,玩转电脑软件开发(二)

    关注今日头条-做全栈攻城狮,学代码也要读书,爱全栈,更爱生活.提供程序员技术及生活指导干货. 如果你真想学习,请评论学过的每篇文章,记录学习的痕迹. 请把所有教程文章中所提及的代码,最少敲写三遍,达到 ...

  6. 程序员带你十天快速入门Python,玩转电脑软件开发(一)

    关注今日头条-做全栈攻城狮,学代码也要读书,爱全栈,更爱生活.提供程序员技术及生活指导干货. 如果你真想学习,请评论学过的每篇文章,记录学习的痕迹. 请把所有教程文章中所提及的代码,最少敲写三遍,达到 ...

  7. 新手如何快速入门Python

    学习任何一门语言都是从入门(1年左右),通过不间断练习达到熟练水准(3到5年),少数人最终能精通语言,成为执牛耳者,他们是金字塔的最顶层.虽然万事开头难,但好的开始是成功的一半,今天这篇文章就来谈谈如 ...

  8. 快速开始Python/WSGI应用程序

    快速开始Python-wsig应用程序 官方参考文档 安装 uwsgi 安装 pip install uwsgi uwsgi --version # 查看 uwsgi 版本 测试 uwsgi 是否正常 ...

  9. python从入门到精通之30天快速学python视频教程

    点击了解更多Python课程>>> python从入门到精通之30天快速学python视频教程 课程目录: python入门教程-1-Python编程语言历史及特性.mkv pyth ...

随机推荐

  1. iOS之NSPredicate(正则表达式和UIBarController)

    本文转发至:https://segmentfault.com/a/1190000000623005 NSPredicate,这个类和我上一篇博文中提到的valueForKeyPath一样很强大.它的使 ...

  2. SpringMVC轻松学习-SpringMVC介绍(一)

    Spring  MVC 背景介绍 Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块.使用 Spring 可插入的 MVC 架构,可以选择是使用内置的 Spring Web 框架还是 ...

  3. HTML 5终于定稿,八年后我们再一次谈谈怎么改变世界

    我们第一次谈论 HTML5 要改变世界大概是因为乔布斯,他坚持在 iOS 上不兼容 Flash,在 Adobe 统治多媒体开发的那个年代,这需要付出极大的勇气.这么多年过去了,虽然所有人都在谈论 HT ...

  4. ios UIKit动力

    UIkit动力学是UIkit框架中模拟真实世界的一些特性. UIDynamicAnimator 主要有UIDynamicAnimator类,通过这个类中的不同行为来实现一些动态特性. 它一般有两种初始 ...

  5. Delphi 与 DirectX

    关于DirectX 在Delphi下的使用 源:Delphi 与 DirectX

  6. matlab取模与取余

    mod函数采用floor,rem函数采用fix函数.那么什么是floor和fix? fix(x):截尾取整.如: >> fix([3.4 , -3.4]) ans = 3 -3 floor ...

  7. Tessnet2图片识别

    验证码识别据说可以用C#图像识别类库Tessnet2来实现,Tessnet2源于目前Google维护的开源项目Tesseract2.本文将对此传说进行验证,含验证结果与验证方法. 1. 验证结果 —— ...

  8. Django中扩展Paginator实现分页

    Reference:https://my.oschina.net/kelvinfang/blog/134342 Django中已经实现了很多功能,基本上只要我们需要的功能,都能够找到相应的包.要在Dj ...

  9. IOS 上线问题

    info.plist 是否支持后台位置 音频 Info.plist中添加UIBackgroundModes键值,它包含一个或多个string的值,包括 audio:在后台提供声音播放功能,包括音频流和 ...

  10. 表单提交checkbox的值

    问题:怎么在表单提交的时候提交多个多选框CheckBox的值? 解决方式:在CheckBox的name属性名后添加[]; 例: <input type="checkbox" ...