python 扩展_用C语言扩展Python的功能_weixin_39901558的博客-程序员宅基地

技术标签: python 扩展  

用C语言扩展Python的功能

如何找到、使用和参与开放源代码项目

肖文鹏

2003 年 2 月 03 日发布

一、简介

Python是一门功能强大的高级脚本语言,它的强大不仅表现在其自身的功能上,而且还表现在其良好的可扩展性上,正因如此,Python已经开始受到越来越多人的青睐,并且被屡屡成功地应用于各类大型软件系统的开发过程中。

与其它普通脚本语言有所不同,Python程序员可以借助Python语言提供的API,使用C或者C++来对Python进行功能性扩展,从而即可以利用Python方便灵活的语法和功能,又可以获得与C或者C++几乎相同的执行性能。执行速度慢是几乎所有脚本语言都具有的共性,也是倍受人们指责的一个重要因素,Python则通过与C语言的有机结合巧妙地解决了这一问题,从而使脚本语言的应用范围得到了很大扩展。

在用Python开发实际软件系统时,很多时候都需要使用C/C++来对Python进行扩展。最常见的情况是目前已经存在一个用C编写的库,需要在Python语言中使用该库的某些功能,此时就可以借助Python提供的扩展功能来实现。此外,由于Python从本质上讲还是一种脚本语言,某些功能用Python实现可能很难满足实际软件系统对执行效率的要求,此时也可以借助Python提供的扩展功能,将这些关键代码段用C或者C++实现,从而提供程序的执行性能。

本文主要介绍Python提供的C语言扩展接口,以及如何使用这些接口和C/C++语言来对Python进行功能性扩展,并辅以具体的实例讲述如何实现Python的功能扩展。

二、Python的C语言接口

Python是用C语言实现的一种脚本语言,本身具有优良的开放性和可扩展性,并提供了方便灵活的应用程序接口(API),从而使得C/C++程序员能够在各个级别上对Python解释器的功能进行扩展。在使用C/C++对Python进行功能扩展之前,必须首先掌握Python解释所提供的C语言接口。

2.1 Python对象(PyObject)

Python是一门面向对象的脚本语言,所有的对象在Python解释器中都被表示成PyObject,PyObject结构包含Python对象的所有成员指针,并且对Python对象的类型信息和引用计数进行维护。在进行Python的扩展编程时,一旦要在C或者C++中对Python对象进行处理,就意味着要维护一个PyObject结构。

在Python的C语言扩展接口中,大部分函数都有一个或者多个参数为PyObject指针类型,并且返回值也大都为PyObject指针。

2.2 引用计数

为了简化内存管理,Python通过引用计数机制实现了自动的垃圾回收功能,Python中的每个对象都有一个引用计数,用来计数该对象在不同场所分别被引用了多少次。每当引用一次Python对象,相应的引用计数就增1,每当消毁一次Python对象,则相应的引用就减1,只有当引用计数为零时,才真正从内存中删除Python对象。

下面的例子说明了Python解释器如何利用引用计数来对Pyhon对象进行管理:例1:refcount.py

class refcount:

# etc.

r1 = refcount() # 引用计数为1

r2 = r1 # 引用计数为2

del(r1) # 引用计数为1

del(r2) # 引用计数为0,删除对象

在C/C++中处理Python对象时,对引用计数进行正确的维护是一个关键问题,处理不好将很容易产生内存泄漏。Python的C语言接口提供了一些宏来对引用计数进行维护,最常见的是用Py_INCREF()来增加使Python对象的引用计数增1,用Py_DECREF()来使Python对象的引用计数减1。

2.3 数据类型

Python定义了六种数据类型:整型、浮点型、字符串、元组、列表和字典,在使用C语言对Python进行功能扩展时,首先要了解如何在C和Python的数据类型间进行转化。

2.3.1 整型、浮点型和字符串

在Python的C语言扩展中要用到整型、浮点型和字符串这三种数据类型时相对比较简单,只需要知道如何生成和维护它们就可以了。下面的例子给出了如何在C语言中使用Python的这三种数据类型:例2:typeifs.c

// build an integer

PyObject* pInt = Py_BuildValue("i", 2003);

assert(PyInt_Check(pInt));

int i = PyInt_AsLong(pInt);

Py_DECREF(pInt);

// build a float

PyObject* pFloat = Py_BuildValue("f", 3.14f);

assert(PyFloat_Check(pFloat));

float f = PyFloat_AsDouble(pFloat);

Py_DECREF(pFloat);

// build a string

PyObject* pString = Py_BuildValue("s", "Python");

assert(PyString_Check(pString);

int nLen = PyString_Size(pString);

char* s = PyString_AsString(pString);

Py_DECREF(pString);

2.3.2 元组

Python语言中的元组是一个长度固定的数组,当Python解释器调用C语言扩展中的方法时,所有非关键字(non-keyword)参数都以元组方式进行传递。下面的例子示范了如何在C语言中使用Python的元组类型:例3:typetuple.c

// create the tuple

PyObject* pTuple = PyTuple_New(3);

assert(PyTuple_Check(pTuple));

assert(PyTuple_Size(pTuple) == 3);

// set the item

PyTuple_SetItem(pTuple, 0, Py_BuildValue("i", 2003));

PyTuple_SetItem(pTuple, 1, Py_BuildValue("f", 3.14f));

PyTuple_SetItem(pTuple, 2, Py_BuildValue("s", "Python"));

// parse tuple items

int i;

float f;

char *s;

if (!PyArg_ParseTuple(pTuple, "ifs", &i, &f, &s))

PyErr_SetString(PyExc_TypeError, "invalid parameter");

// cleanup

Py_DECREF(pTuple);

2.3.3 列表

Python语言中的列表是一个长度可变的数组,列表比元组更为灵活,使用列表可以对其存储的Python对象进行随机访问。下面的例子示范了如何在C语言中使用Python的列表类型:例4:typelist.c

// create the list

PyObject* pList = PyList_New(3); // new reference

assert(PyList_Check(pList));

// set some initial values

for(int i = 0; i < 3; ++i)

PyList_SetItem(pList, i, Py_BuildValue("i", i));

// insert an item

PyList_Insert(pList, 2, Py_BuildValue("s", "inserted"));

// append an item

PyList_Append(pList, Py_BuildValue("s", "appended"));

// sort the list

PyList_Sort(pList);

// reverse the list

PyList_Reverse(pList);

// fetch and manipulate a list slice

PyObject* pSlice = PyList_GetSlice(pList, 2, 4); // new reference

for(int j = 0; j < PyList_Size(pSlice); ++j) {

PyObject *pValue = PyList_GetItem(pList, j);

assert(pValue);

}

Py_DECREF(pSlice);

// cleanup

Py_DECREF(pList);

2.3.4 字典

Python语言中的字典是一个根据关键字进行访问的数据类型。下面的例子示范了如何在C语言中使用Python的字典类型:例5:typedic.c

// create the dictionary

PyObject* pDict = PyDict_New(); // new reference

assert(PyDict_Check(pDict));

// add a few named values

PyDict_SetItemString(pDict, "first",

Py_BuildValue("i", 2003));

PyDict_SetItemString(pDict, "second",

Py_BuildValue("f", 3.14f));

// enumerate all named values

PyObject* pKeys = PyDict_Keys(); // new reference

for(int i = 0; i < PyList_Size(pKeys); ++i) {

PyObject *pKey = PyList_GetItem(pKeys, i);

PyObject *pValue = PyDict_GetItem(pDict, pKey);

assert(pValue);

}

Py_DECREF(pKeys);

// remove a named value

PyDict_DelItemString(pDict, "second");

// cleanup

Py_DECREF(pDict);

三、Python的C语言扩展

3.1 模块封装

在了解了Python的C语言接口后,就可以利用Python解释器提供的这些接口来编写Python的C语言扩展,假设有如下一个C语言函数:例6:example.c

int fact(int n)

{

if (n <= 1)

return 1;

else

return n * fact(n - 1);

}

该函数的功能是计算某个给定自然数的阶乘,如果想在Python解释器中调用该函数,则应该首先将其实现为Python中的一个模块,这需要编写相应的封装接口,如下所示:例7: wrap.c

#include

PyObject* wrap_fact(PyObject* self, PyObject* args)

{

int n, result;

if (! PyArg_ParseTuple(args, "i:fact", &n))

return NULL;

result = fact(n);

return Py_BuildValue("i", result);

}

static PyMethodDef exampleMethods[] =

{

{"fact", wrap_fact, METH_VARARGS, "Caculate N!"},

{NULL, NULL}

};

void initexample()

{

PyObject* m;

m = Py_InitModule("example", exampleMethods);

}

一个典型的Python扩展模块至少应该包含三个部分:导出函数、方法列表和初始化函数。

3.2 导出函数

要在Python解释器中使用C语言中的某个函数,首先要为其编写相应的导出函数,上述例子中的导出函数为wrap_fact。在Python的C语言扩展中,所有的导出函数都具有相同的函数原型:PyObject* method(PyObject* self, PyObject* args);

该函数是Python解释器和C函数进行交互的接口,带有两个参数:self和args。参数self只在C函数被实现为内联方法(built-in method)时才被用到,通常该参数的值为空(NULL)。参数args中包含了Python解释器要传递给C函数的所有参数,通常使用Python的C语言扩展接口提供的函数PyArg_ParseTuple()来获得这些参数值。

所有的导出函数都返回一个PyObject指针,如果对应的C函数没有真正的返回值(即返回值类型为void),则应返回一个全局的None对象(Py_None),并将其引用计数增1,如下所示:PyObject* method(PyObject *self, PyObject *args)

{

Py_INCREF(Py_None);

return Py_None;

}

3.3 方法列表

方法列表中给出了所有可以被Python解释器使用的方法,上述例子对应的方法列表为:static PyMethodDef exampleMethods[] =

{

{"fact", wrap_fact, METH_VARARGS, "Caculate N!"},

{NULL, NULL}

};

方法列表中的每项由四个部分组成:方法名、导出函数、参数传递方式和方法描述。方法名是从Python解释器中调用该方法时所使用的名字。参数传递方式则规定了Python向C函数传递参数的具体形式,可选的两种方式是METH_VARARGS和METH_KEYWORDS,其中METH_VARARGS是参数传递的标准形式,它通过Python的元组在Python解释器和C函数之间传递参数,若采用METH_KEYWORD方式,则Python解释器和C函数之间将通过Python的字典类型在两者之间进行参数传递。

3.4 初始化函数

所有的Python扩展模块都必须要有一个初始化函数,以便Python解释器能够对模块进行正确的初始化。Python解释器规定所有的初始化函数的函数名都必须以init开头,并加上模块的名字。对于模块example来说,则相应的初始化函数为:void initexample()

{

PyObject* m;

m = Py_InitModule("example", exampleMethods);

}

当Python解释器需要导入该模块时,将根据该模块的名称查找相应的初始化函数,一旦找到则调用该函数进行相应的初始化工作,初始化函数则通过调用Python的C语言扩展接口所提供的函数Py_InitModule(),来向Python解释器注册该模块中所有可以用到的方法。

3.5 编译链接

要在Python解释器中使用C语言编写的扩展模块,必须将其编译成动态链接库的形式。下面以RedHat Linux 8.0为例,介绍如何将C编写的Python扩展模块编译成动态链接库:[[email protected] code]$ gcc -fpic -c -I/usr/include/python2.2 \

-I /usr/lib/python2.2/config \

example.c wrapper.c

[[email protected] code]$ gcc -shared -o example.so example.o wrapper.o

3.6 引入Python解释器

当生成Python扩展模块的动态链接库后,就可以在Python解释器中使用该扩展模块了,与Python自带的模块一样,扩展模块也是通过import命令引入后再使用的,如下所示:[[email protected] code]$ python

Python 2.2.1 (#1, Aug 30 2002, 12:15:30)

[GCC 3.2 20020822 (Red Hat Linux Rawhide 3.2-4)] on linux2

Type "help", "copyright", "credits" or "license" for more information.

>>> import example

>>> example.fact(4)

24

>>>

四、结束语

作为一门功能强大的脚本语言,Python将被更加广泛地应用于各个领域。为了克服脚本语言执行速度慢的问题,Python提供了相应的C语言扩展接口,通过将影响执行性能的关键代码用C语言实现,可以很大程度上提高用Python编写的脚本在运行时的速度,从而满足实际需要。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_39901558/article/details/110887913

智能推荐

Ubuntu下搭建,使用FTP服务器图解_hxts的博客-程序员宅基地

除了SCP能够远程传输,Ubuntu还能建立ftp进行远程传输文件

Python碎片化学习教程 @1. Python简介_愚者黑科技工作室的博客-程序员宅基地

  作为这套《Python碎片化学习教程》的第一讲,我们先来了解一下Python是个什么东西、有什么用处、相对其他编程语言大概有哪些优点、主要应用领域这些问题。学之前需要对学习的对象有一个基本的认识、要对它有概念,这是必要的,也是必须的。  Python这个单词在英语里的意思是蟒、蚺蛇的意思,英式发音读作[ˈpaɪθən]、美式发音读作[ˈpaɪθɑːn]。之所以取这个名字,据说是因为作者受当时一部电视剧的影响。关于Python的发音,其实在很多人那里都是不同的,怎么读的都有,没人说得清哪种是对的哪种是错

当画面只有一个属性type是“text”的输入框的时候,敲击回车键会触发按钮提交事件_脸¹点也不○的博客-程序员宅基地

项目场景:有一个登录画面,画面项目包括一个输入框和一个按钮:入力框:&lt;input class="input" type="text" name="userName" maxlength="4" placeholder="0123"/&gt;按钮框:&lt;button id="submitBtn" type="button"&gt;下一步&lt;/button&gt;问题描述:操作步骤: 进入到登录画面,敲击回车键的时候,会出现页面跳转。原因分析:当画面只有一个属性t

Linux替换gcc,多版gcc下替换系统原版gcc要注意的事项_weixin_39966602的博客-程序员宅基地

多版gcc下替换系统原版gcc要注意的事项发布时间:2007-08-12 21:06:33来源:红联作者:JinXian众所周知,gcc 是可以多版同存的,因此很多时候为了测试新版 gcc 与及避免对原来系统影响太多的前题下,我会将新的 gcc 装到 /opt 里去(configure 的时候用 --prefix=/opt/gcc-xxx),然後在 /usr/bin 下使用 symlink 方式来...

think php 缩放图片,THINKPHP+JS缩放图片截图的实现_weixin_39942474的博客-程序员宅基地

THINKPHP+JS缩放图片截图的实现发布于 2014-07-24 22:04:35 | 529 次阅读 | 评论: 0 | 来源: 网友投递ThinkPHP开源PHP框架ThinkPHP是一个开源的PHP框架,是为了简化企业级应用开发和敏捷WEB应用开发而诞生的。ThinkPHP可以支持windows/Unix/Liunx等服务器环境,正式版需要PHP5.0以上版本支持,支持MySql、PgS...

java 删除数据库_【Java】执行数据库的删除操作_Geek7even的博客-程序员宅基地

import java.sql.*;//执行数据库的删除操作public class DeleteData {//定义MySQL的数据库驱动程序public static final String DBDRIVER="org.gjt.mm.mysql.Driver";//定义MySQL数据库的连接地址,3306为mysql数据库的端口号,user为数据库名称public static final ...

随便推点

如何查看虚拟服务器数据库,如何优化虚拟数据库服务器_Claire離離小姐的博客-程序员宅基地

要优化虚拟数据库通过监视虚拟您应该避免过度使用VM内存或处理器以确保虚拟数据库服务器的最佳性能。您还应该保持较低的进程数,以防止VM使用太多资源。保持数据库进程低的一种方法是让一些其他VM在重要的虚拟数据库服务器上运行。保持数据库进程低的另一种方法是从头开始安装来宾操作系统而不是转换它们。这可确保不会留下仅在物理环境中必需的进程。过度使用内存可能会导致潜在的性能问题,例如CPU等待时间增加,延迟或...

【java笔试系列六】HashMap常见面试问题总结_米殇粟的博客-程序员宅基地

“你用过HashMap吗?” “什么是HashMap?你为什么用到它?”几乎每个人都会回答“是的”,然后回答HashMap的一些特性,譬如HashMap可以接受null键值和值,而Hashtable则不能;HashMap是非synchronized;HashMap很快;以及HashMap储存的是键值对等等。这显示出你已经用过HashMap,而且对它相当的熟悉。但是面试官来个急转直下,从此刻

新学习的语言Groovy_weixin_34268753的博客-程序员宅基地

什么是 Groovy?Groovy 是 JVM 的一个替代语言 —替代 是指可以用 Groovy 在 Java 平台上进行 Java 编程,使用方式基本与使用 Java 代码的方式相同。在编写新应用程序时,Groovy 代码能够与 Java 代码很好地结合,也能用于扩展现有代码。目前的 Groovy 版本是 1.5.4,在 Java 1.4 和 Java 5 平台上都能使用,也能在 Java ...

微信自定义分享功能实现_GG非常废的博客-程序员宅基地_微信自定义分享

微信自定义分享功能实现微信自定义分享功能实现一、实现的关键1.后端的任务2.前台的任务二、实现具体步骤1、js安全域名配置(被分享的网址必须实现)2、添加服务器配置(成为开发者)3、生成签名signature(1)获取access_Token:(2)获取 jsapi_ticket :(3)用sha1加密算法得到signature:4、将参数发送到前台:(1)后台总体方法:(2)前台JavaScript代码:三、踩过的坑:(1)获取access_token时报错,提示某某IP不在白名单内:(2)公众号是个人订

搬砖之路-转场动画ActivityOptions_li419360214的博客-程序员宅基地

接触场景: 开发文档里要求的。需求:点击列表里的item图片,放大跳转到大图界面,关闭大图界面时,缩小返回至列表里的item图片处实现: 使用ActivityOptions.makeSceneTransitionAnimation流程: 1、在清单文件里的跳出activity的主题里 添加 <item name="android:windowAllowEnterTransitionOverla

高级运维工程师的打怪升级之路_weixin_34343308的博客-程序员宅基地

初级1、Linux基础刚开始阶段需要熟悉Linux/Windows操作系统安装,目录结构、启动流程等。2、系统管理主要学习Linux系统,生产环境中基本都在字符界面完成工作,所以要掌握常用的几十个基本管理命令,包括用户管理、磁盘分区、软件包管理、文件权限、文本处理、进程管理、性能分析工具等。3、网络基础OSI和TCP/IP模型一定要熟悉。基本的交换机、路由器概念及实现原理要知道。4、Shel...

推荐文章

热门文章

相关标签