相关背景
python作为一种脚本语言,底层数是基于c/c++实现的。在日常开发中,难免会遇到一些需要但没有的模块。这时候就需要自己编写。现在的Python第三方模块基本就是这么实现的。
要编写出一个这样的模块需要两个环节,用c/c++实现相关功能,然后封装Python接口。本教程是在假设已经完成了功能实现的基础上,获得了.h和.c文件之后,教学如何封装成可供Python直接调用的第三方模块。
Note:功能实现的时候,必须将声明和实现分开,因为封装接口的时候其实是只看.h文件中的函数声明的。此外可以单独写main文件测试功能,但是不要和实现放到一起。
swig原理
本教程所使用的工具就是swig。swig本质是一个代码生成器,生成c/c++到其他语言的包装代码。而生成的过程是按照一定的规则来的,我们的任务就是去书写这个规则。(整个过程很类似书写makefile,规定build代码的细节,不过比写makefile要简单很多)
而这个规则文件的后缀是一个.i,也就是在这一步我们会获得一个.i文件。
快速实现
经过前面的铺垫,我们知道要用c++完成一个Python模块的书写,需要三个文件,功能实现的.h和.c文件以及,生成接口代码的规则文件.i文件。
可以参照这个教程直接粘贴复制代码,快速实践整个流程。特别注意教程中最后生成模块的步骤中书写setup.py代码时候的注意事项。也就是如下图所示环节:
否则会报如下错误:ImportError: dynamic module does not define init function
内容补充
swig规则说明
以教程中的.i文件为例
%module example
#模块名字
%{
#define SWIG_FILE_WITH_INIT
#include “example.h”
%}
#要封装的接口文件,也就是.h文件提供相应的接口,也即是函数声明,而到时候会直接依据这些接口封装Python接口,所以所.h文件必须要独立出来
int fact(int n);
#要封装的接口,前面include了.h文件就好像我们写c代码的时候include了一个标准库头文件,但是我们不会说把所有在头文件中声明的函数都用完,只会使用其中一些(当然想用可以用完)。所以这里的作用也是一样,我们不一定要疯转所有的接口可能只想封装几个,那么就把要封装的接口罗列出来就可以。
此外我们还可以include一些其他的.i文件进来,比如你想让你的新模块能够使用c++中的string类型,你不必自己写一个,可以直接include “std_string.i”,因为swig官方对于一些常用的c++接口已经提供了相应的封装文件,可以直接调用进来就可以。
具体封装了那些接口可以看这个链接
在Python中调用自定义类对象
前面介绍了如何调用函数,那么c++中另一个重要的东西类怎么调用呢?
首先编写,封装的步骤是一样的。假设现在有如下三个文件:
swig_test.h文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using std::cout;
using std::cin;
using std::endl;
int fact(int n);
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
double getVolume(void);
void setLength( double len );
void setBreadth( double bre );
void setHeight( double hei );
};
swig_test.c文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int fact(int n){
if(n<0){
return 0;
}
if(n==0){
return 1;
}
else{
return (1+n)*fact(n-1);
}
}
double Box::getVolume(void)
{
return length * breadth * height;
}
void Box::setLength( double len )
{
length = len;
}
void Box::setBreadth( double bre )
{
breadth = bre;
}
void Box::setHeight( double hei )
{
height = hei;
}
swig_test.i文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21%module swig_test
%{
%}
int fact(int n);
class Box
{
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
double getVolume(void);
void setLength( double len );
void setBreadth( double bre );
void setHeight( double hei );
};
然后调用c++中类的方式如下:
a=swig_test.Box()
这样就能生成一个类对象,然后调用其方法对其进行操作,比如:
a.setLength(3)
生成python3的模块
之前的方式生成的模块只是供Python2调用的,如果需要生成一个可供python3调用的,在执行swig命令的时候应该如下:
swig -c++ -python -py3 swig_test.i
指明是python3
将模块加入Python搜索目录
目前生成的模块,只能当前文件夹下检索,如果在其他位置写了一个Python文件,想要调用这个模块只做不到的,除非把它加入到整个Python环境下的检索目录,或说在代码中指明可以在存放这个模块的文件夹中检索。
不管那种方式都需要用到sys这个模块。
使用sys.path可以看到Python环境下的检索目录,将生成的模块随意放到其中一个即可
或者在代码中使用sys.path.append(“file_road”),file_road是模块所在文件夹位置,也可以实现访问
操作系统问题
这个教程只在ubuntu下使用,在Windows上使用,也许会发生 unable to find vcvarsall.bat