使⽤python创建⽣成动态链接库dll
如今,随着深度学习的发展,python已经成为了深度学习研究中第⼀语⾔。绝⼤部分的深度学习⼯具包都有python的版本,很多重要算法都有python版本的实现。为了将这些算法应⽤到具体⼯程中,这些⼯具包也提供了不同类型的接⼝。
动态链接库(.dll,.so)是系统开发中⼀种⾮常重要的跨语⾔协作⽅式。把python语⾔写成的算法编译成动态库,能够提供给其他语⾔调⽤,这能够在很⼤程度上提⾼算法的开发效率。
但是,虽然python可以调⽤其他语⾔⽣成的动态库,python作为⼀种脚本语⾔,本⾝是不能直接编译⽣成动态库的。为了⽣成动态库,我们借助cython,将python脚本变成c语⾔⽂件。具体过程,我们通过⼀个简单的例⼦来解释。
def str_add(str1,str2):
return int(str1) + int(str2)
  这个代码,将两个数字组成的字符串转化成数字,并求和。我们把这个代码保存成run.py备⽤。根据cython的语法,我们给出cython版本的函数:
cdef public str_add(str1,str2):
return int(str1) + int(str2)
  和前⾯python版本的相⽐,cdef替换了def,并加了public关键字,表⽰这个函数要导出。将这个代码保存成pyx⽂件,⽐如run.pyx。
接下来,我们执⾏如下命令,把这个代码变成c语⾔版本:
cython run.pyx
这时,⽬录下⾯⽣出来run.h和run.c两个⽂件。这个两个⽂件通过调⽤python的C-API实现了run.py代码的功能。
接下来,我们编写动态库的主⽂件dllmain.c:
#include <Python.h>
#include <Windows.h>
#include "run.h"
extern __declspec(dllexport) int __stdcall _str_add(const char * a, const char * b) {
return PyLong_AsLong(str_add(PyUnicode_FromString(a),PyUnicode_FromString(b)));
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpReserved) {
switch( fdwReason ) {
case DLL_PROCESS_ATTACH:
Py_Initialize();
PyInit_run();  #dll初始化的时候调⽤,这是python3的写法,python2改成,initrun()。参见⽣成的run.h
break;
case DLL_PROCESS_DETACH:
Py_Finalize();
break;
}
return TRUE;
}
  该⽂件定义了导出函数_str_add。在python中,所有数据都以pyobject进⾏存储。这个函数通过PyUnicode_FromString,将两个字符串变成python对象类型,并调⽤run.h⾥⾯的函数str_add求和,并把结果通过PyLong_AsLong函数从python对象,变成整形数字。
我们可以通过如下命令,将这个代码编译⽣成dll:
cl /LD dllmain.c run.c -IC:\python36\include C:\python36\libs\python36.lib
这⾥python的路径,根据不同电脑python的安装位置,做相应调整。
⽣成的dll,我们写个简单调⽤,测试⼀下:
#include "stdio.h"
#include "stdlib.h"
extern __declspec(dllexport) int __stdcall _str_add(const char * a, const char * b);
#pragma comment(lib,"dllmain.lib")
int main()
{
printf("%d \n", _str_add("123","456"));
return 0;
}
  输出结果: 579,正好等于123+456。
通过以上步骤,我们已经能够把python代码实现的功能,封装成动态库。然⽽,这个动态库⽆法在没有安装python的机器上⾯运⾏。事实
上,python代码,通常需要很多依赖包才能运⾏。⽽且,每段代码需要的依赖包是不⼀样的。为了查这些包,我们采⽤另外⼀个⼯具pyinstaller。具体步骤简介如下:
printf函数的用法python
1. virtualenv envpack # 创建新的环境,python包依赖⽐较复杂,创建新环境可以减少最终引⼊的包
2. cd envpack # 进⼊⽬录
3. #复制run.py到这个⽬录,run.py运⾏需要的包,和最终dll需要的包是⼀样的
4. Scripts\activate # 激活并切换到virtualenv环境
5. pip install pyinstaller # 安装打包⼯具pyinstaller
6. pip install numpy # 安装numpy等脚本需要的库,查看你的import
7. pyinstaller run.py # 打包命令
8. Scripts\deactivate # 打包成功后,使⽤命令取消激活环境
9. 需要打包的⽂件在envpack\dist,包括很多.dll和.pyd⽂件,把这些⽂件和dll⼀起发布即可。