参考链接: 如何在Python中调用C函数

目录

 编写Python扩展1. 创建应用代码2. 根据样板编写封装代码2.1 包含Python头文件2.2 为每一个模块函数添加形如PyObject* *Module_func()* 的封装函数2.3 为每一个模块函数添加一个PyMethodDef *ModuleMethods[]* 数组/表2.4 添加模块初始化函数

   3 编译并测试3.1 创建 setup.py3.2 运行 setup.py 来编译并链接代码3.3 在Python中导入模块3.4 测试函数

   4. 文档扩展4.1 在2.2中出现的 `int PyArg_ParseTuple(PyObject *arg, const char *format, ...);` 函数Python和C/C++之间的“转换编码”取自文档的例子:文档1.7部分

    4.2 在3.1中创建的`setup.py` 。

  

 

 

 

编写Python扩展 

1. 创建应用代码 

 编写一个比较简单的函数,求阶乘。并且写上main函数进行测试。   编译测试  

$ gcc Extest1.c -o Extest
$ ./Extest
4! = 24

 

2. 根据样板编写封装代码 

先给个文档地址:Extending Python with C or C++ 

2.1 包含Python头文件 

在大多数类UNIX系统上,Python包含文件一般位于/usr/local/include/python2.x 或者 /usr/include/python2.x 中。我的系统是ubuntu 18.04 LTS。没有python2,系统自动安装的只有python3。我的位于 /usr/include/python3.6 中 将Python.h 这个头文件包含在源码中 

#include "Python.h"

 

这边之后编译这个文件时可能会出现错误 fatal error: Python.h: No such file or directory compilation terminated. 。 stack overflow上的解决方案 

sudo apt-get install python3-dev

 

2.2 为每一个模块函数添加形如PyObject* Module_func() 的封装函数 

对于每个需要在Python环境中访问的函数,需要创建一个以static PyObjuec* 标识,以模块名开头,紧接着是下划线和函数名本身的函数。例如,若要让 fac() 函数可以在Python中导入,并将Extest 作为最终的模块名称,需要创建一个名为Extest_fac 的封装函数。在用到这个函数的Python脚本中,可以使用import Extest 和 Extest.fac() 的形式在任意地方调用fac() 函数。封装函数的任务是将Python中的值转成C形式,接着调用相应的函数。当C函数执行完毕时,需要返回Python的环境中。封装函数需要将返回值转换成Python形式,并进行真正的返回,传回所有需要的值。  

2.3 为每一个模块函数添加一个PyMethodDef ModuleMethods[] 数组/表 

 

这边书里《Python核心编程》和文档稍微有点出入,书里是3个参数,文档是4个参数,但是文档没有写第4个参数是什么,所以这边先按书里写的。 

2.4 添加模块初始化函数 

书里是这么写的,我怀疑是python2和3的区别,所以这里书里的不太对,我这边在后面的运行中会出现问题,所以这里稍微改一下。用文档的内容。 文档里的初始化格式:  

3 编译并测试 

3.1 创建 setup.py 

 

3.2 运行 setup.py 来编译并链接代码 

运行该命令构建扩展 

python3 setup.py build

 

 

Error 1: 找不到python.h。解决方案见上Error 2: 用书里的函数会显示没有initModule这个函数。解决方案见上Error 3: 显示找不到distutils 模块No module named 'distutils.core'。解决方案 

sudo apt-get install python3-distutils

 

3.3 在Python中导入模块 

扩展模块会创建在build/lib.*目录下,即运行setup.py脚本的位置。要么切换到这么目录中,要么用下面的方式将气安装到Python中。 

$ python3 setup.py install

 

 

Error 这里有了问题,注意最后一行Permission denied ,所以这里用 sudo python3 setup.py install 就可以了。 

3.4 测试函数 

 

4. 文档扩展 

4.1 在2.2中出现的 int PyArg_ParseTuple(PyObject *arg, const char *format, ...); 函数 

函数文档 1.7在fac()的示例中,当客户程序调用Extest.fac()时,会调用封装函数。这里会接受一个Python整数,将其转换成C整数,接着调用C函数fac(),获取返回结果,同样是一个整数。将这个返回值转换成Python整数,返回给调用者(记住,编写的封装函数就是def fac(n)声明的代理函数。当这个封装函数返回时,就相当于Python fac()函数执行完毕了)。如何完成转换?Python -> C : PyArg_Parse*(). C->Python : Py_BuildValue()PyArg_Parse*() : 类似于C中的sscanf 函数。接受一个字节流,然后根据一些格式字符串进行解析,将结果放入到相应指针所指的变量中。若解析成功就返回1;否则返回0.Py_BuildValue() :类似于 sprintf 函数,接受一个格式字符串,并将所有参数按照格式字符串指定的格式转换成一个Python 对象。 

Python和C/C++之间的“转换编码” 

格式编码Python数据类型C/C++数据类型s, s#str/unicode, len()char*, intiintintbintcharcstrchardfloatdoublehintshortlintlong

取自文档的例子:文档1.7部分 

#include "Python.h"
int ok;
int i, j;
long k, l;
const char *s;
Py_ssize_t size;
 
ok = PyArg_ParseTuple(args, ""); /* No arguments */
    /* Python call: f() */
 
ok = PyArg_ParseTuple(args, "s", &s); /* A string */
    /* Possible Python call: f('whoops!') */
 
ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* Two longs and a string */
    /* Possible Python call: f(1, 2, 'three') */
 
ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);
    /* A pair of ints and a string, whose size is also returned */
    /* Possible Python call: f((1, 2), 'three') */
 
{
    const char *file;
    const char *mode = "r";
    int bufsize = 0;
    ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);
    /* A string, and optionally another string and an integer */
    /* Possible Python calls:
       f('spam')
       f('spam', 'w')
       f('spam', 'wb', 100000) */
}
 
{
    int left, top, right, bottom, h, v;
    ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",
             &left, &top, &right, &bottom, &h, &v);
    /* A rectangle and a point */
    /* Possible Python call:
       f(((0, 0), (400, 300)), (10, 10)) */
}

 

4.2 在3.1中创建的setup.py 。 

官方文档在这里Writing the Setup Script 

参考书籍: 《Python核心编程》Unit8 百度网盘链接:https://pan.baidu.com/s/1XYSY65_BuQkEHm-LFzzi3g 提取码:awqc 《Python3文档》https://docs.python.org/3/ 这篇文章主要需要用到的文档内容上面已经给了