【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
", fac(3)); 
    printf("6! = %d
", fac(6)); 
    printf("9! = %d
", fac(9)); 

    strcpy(s, "abcdefg"); 
    printf("'abcdefg' after reversing is '%s'
", reverse(s)); 
    strcpy(s, "python"); 
    printf("'python' after reversing is '%s'
", 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。

原文地址:https://www.cnblogs.com/gccbuaa/p/7159025.html