【Python】python扩展

当python的基本功能无法满足要求,或者是为了保密源代码(.py)、遇到性能瓶颈时,我们常常要扩展python,扩展语言可以是C/C++、Java、C#等。

为python创建扩展需要三个主要的步骤:创建应用程序代码;利用样板来包装代码;编译与测试。

1、 创建应用程序代码

我们创建一个C代码PythonEx.c,实现两个函数fac()和reverse(),分别用来求阶乘和逆转字符串,test()函数是用来测试fac()和reverse()基本功能的,以防问题带入python。

// PythonEx.c
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

int fac(int n) 
{ 
    if (n < 2) { 
        return (1); 
    } 
    else { 
        return (n) * fac(n - 1); 
    } 
} 

char* reverse(char *s) 
{ 
    register char t; 
    register char *p = s; 
    register char *q = (s + strlen(s) -1); 

    while (p < q) { 
        t = *p; 
        *p++ = *q; 
        *q-- = t; 
    } 

    return s; 
} 

int test() 
{ 
    char s[BUFSIZ]; 

    printf("3! = %d\n", fac(3)); 
    printf("6! = %d\n", fac(6)); 
    printf("9! = %d\n", fac(9)); 

    strcpy(s, "abcdefg"); 
    printf("'abcdefg' after reversing is '%s'\n", reverse(s)); 
    strcpy(s, "python"); 
    printf("'python' after reversing is '%s'\n", reverse(s)); 

    return 0; 
}

2、 用样板包装代码

样板是扩展代码与python解释器之间进行交互的桥梁,主要分为以下4步。

a.包含python的头文件。
找到python头文件位置并确保编译器的访问权限,然后在代码中inlcude这个头文件,如下:

#include “Python.h”

b.为每个模块的每一个函数增加一个型如PyObject* Module_func()的包装函数。
这部分需要为所有想被python环境访问的函数都增加一个静态的函数,函数的返回值类型为PyObject*,函数名前面要加上模块名和一个下划线。包装函数的用处就是先把python的值传递给C,然后调用我们想要调用的相关函数,当这个函数完成要返回python的时候,把函数的计算结果转换成python的对象,然后返回给python。那么,在从python到C的转换就用PyArg_Parse*系列函数,在从C转到python的时候,就用Py_BuildValue()函数。
下面我们包装fac()和reverse()函数,假设其模块名为CustomPy,如下:

static PyObject* CustomPy_fac(PyObject *self, PyObject *args) 
{ 
    int num; 
    if (!PyArg_ParseTuple(args, "i", &num)) { // i i.e. int->int 
        return NULL; 
    } 
    return (PyObject*)Py_BuildValue("i", fac(num)); // i i.e. int->int 
} 

static PyObject* CustomPy_reverse(PyObject *self, PyObject *args) 
{ 
    char *orig_str; // original 
    char *dup_str; // reversed 
    PyObject *retval; 
    if (!PyArg_ParseTuple(args, "s", &orig_str)) { // s i.e. str->char* 
        return NULL; 
    } 
    retval = (PyObject*)Py_BuildValue( 
            "ss", // s i.e. char*->str 
            orig_str, 
            dup_str = reverse(strdup(orig_str))); // strdup 
    free(dup_str); // free after strdup 
    return retval; // return tuple(orig_str, dup_str) 
}

static PyObject* CustomPy_test(PyObject *self, PyObject *args) 
{ 
    test(); 
    return (PyObject*)Py_BuildValue(""); 
}

下面是Python和C/C++之间的数据格式——
FormatCode PythonType C/C++Type
s str char*
z str/None char*/NULL
i int int
l long long
c str char
d float double
D complex Py_Complex*
o (any) PyObject*
S str PyStringObject

c.为每个模块增加一个型如PyMethodDef ModuleMethods[]的数组。

static PyMethodDef CustomPyMethods[] = { 
    {"fac", CustomPy_fac, METH_VARARGS}, 
    {"reverse", CustomPy_reverse, METH_VARARGS}, 
    {"test", CustomPy_test, METH_VARARGS}, 
    {NULL, NULL}, 
};

完成包装函数后,把他们加入到一个数组中,以便于python解释器能够导入并调用它们。每一个数组都包含了函数在python中的名字、相应的包装函数的名字以及一个METH_VARARGS常量,这个常量表示参数以tuple形式传入。最后是一个NULL数组表示列表结束。

d.增加模块初始化函数void initModule()

void initCustomPy() 
{ 
    Py_InitModule("CustomPy", CustomPyMethods); 
}

这部分代码在模块被导入的时候被解释器调用。

另外,创建扩展还可以先写包装代码,使用桩函数、测试函数或哑函数,在开发过程中慢慢地把这些函数用有实际功能的函数替换。

3、编译与测试

为了让新python扩展能被创建,需要把它们与python库放在一起编译,以前可能要用到Makefile,现在使用distutils模块就可以了,能够方便地编译、安装和分发这些模块、扩展和包,只需创建一个setup.py脚本就行。

#!/usr/bin/env python 

from distutils.core import setup, Extension 

MOD = 'CustomPy' 
setup(name = MOD, ext_modules = [Extension(MOD, sources = [PythonEx.c])])

下面直接运行setup.py脚本来创建自定义模块,或者是install到python环境里,最终会生成一个so文件,接着import这个模块就可以使用了。

还有两点需要注意的是,python的垃圾自动回收策略是引用计数,以及线程安全操作,这些都可以加入到自定义的模块中。

上面的例子中用C扩展了Python,我们还可以用Java扩展Jython,使用C#或者VB .NET扩展IronPython。如果在win32下,python还可以使用其COM(市场名字为ActiveX)操作Microsoft Office。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值