目录
- 写在前面
- 在vs中建立一个工程
- 设置调整
- step1 选择输出目录
- step2 改变目标文件拓展名
- step3 添加附加目录
- step4 添加附加库目录
- step5添加附加依赖项
- 知识储备
- 条件编译
- 简单例程
- 程序测试
- 补充说明
写在前面
阅读这篇文章需要一定的C/C++和Python基础,阅读完这篇文章,你将能够开发简单的Python的库。 笔者所使用的C++编译器是vs2017,所使用的Python版本是Python3.6-64。Python有Python2和Python3两个主流版本,而预计2020年,Python2可能就停止维护。这里仅介绍如何开发Python3库。如果你是参照文本来写Python库,请务必使用Python3。
在vs中建立一个工程
选择建立一个动态链接库,这个时候你可以先记下这句话,Python的核心即是动态链接库。当你读完这篇文章时,你会对这句话有更深的体会。
在配置中先选中所有配置,因为在最后发布之前应选择Debug模式,生成用Release生成你所需要的Python库
设置调整
以下过程不分先后
step1 选择输出目录
你可以选择一个目录作为输出目录,但不是绝对的,但必须确保,这个目录是能被Python正常访问的,把pyd文件生成在dlls目录下。
step2 改变目标文件拓展名
将目标文件扩展名改.pyd。.
step3 添加附加目录
在附加包含目录下导入Python的include文件,这个文件下包含了<Python.h>文件
step4 添加附加库目录
在编译python库时,会需要调用到python的lib库
将这个目录添加到连接器->常规->目录
step5添加附加依赖项
在链接器->输入->附加依赖项中添加对应的lib文件,教程中使用的是python3.6版本则导入python36.lib
知识储备
条件编译
你很辛苦的开发了一个Python3的库,你自然不希望那个使用Python2的使用者因为使用你的库而造成麻烦,这是最简单的仅编译Python 3的代码
#if PY_MAJOR_VERSION >= 3
//dosomething
#endif
简单例程
通过这个例程你会学会用python生成一个简单的库,并调用这个库中生成的函数 ; 例程中将生成一个返回值为正态分布的随机数,使用的算法是Box-Muller变换。
// first_pyd.cpp : 定义 DLL 应用程序的导出函数。
//
#include <Python.h>
#include <cmath>
const double pi = 3.1415926535897932;
//还有一种写法
//const double pi = 4.0*atan(1.0);
typedef struct module_state{
PyObject *error;
}module_state_,*p_module_state;
#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
static PyObject *
error_out(PyObject *m) {
struct module_state *st = GETSTATE(m);
PyErr_SetString(st->error, "something bad happened");
return NULL;
}
int Extension_Traverse(PyObject * m, visitproc visit, void * arg) {
Py_VISIT(GETSTATE(m)->error);
return 0;
}
int Extension_Clear(PyObject * m) {
Py_CLEAR(GETSTATE(m)->error);
return 0;
}
#ifdef __cplusplus
extern "C" {
#endif
double RANDN(double rate) {
double x1, x2, y1;
x1 = rand() % RAND_MAX / (double)RAND_MAX;
x2 = rand() % RAND_MAX / (double)RAND_MAX;
y1 = rate*sqrt(-2 * log(x1))*cos(2 * pi*x2);
return y1;
}
PyObject* WRANDN(PyObject * self, PyObject * args) {
double rate;
if (!PyArg_ParseTuple(args, "d", &rate))
{
return NULL;
}
else {
return Py_BuildValue("d", RANDN(rate));
}
}
#ifdef __cplusplus
}
#endif
static PyMethodDef ExtendMethods[] = {
{"error_out", (PyCFunction)error_out, METH_NOARGS, NULL},
{"RANDN",WRANDN,METH_VARARGS,"a function from C"},
{NULL,NULL,0,NULL},
};
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"first_pyd",
NULL,
sizeof(module_state),
ExtendMethods,
NULL,
Extension_Traverse,
Extension_Clear,
NULL,
};
PyMODINIT_FUNC
PyInit_first_pyd(void)
{
PyObject *module = PyModule_Create(&moduledef);
if (NULL == module) {
return NULL;
}
p_module_state st = GETSTATE(module);
st->error = PyErr_NewException("first_pyd.Error", NULL, NULL);
if (NULL == st->error) {
Py_DECREF(module);
return NULL;
}
return module;
}
程序测试
import first_pyd
first_pyd.RANDN(5)
补充说明
pyd是一种特殊的dll,但生成pyd文件的方式与传统dll文件,有读者问过dllmain.cpp文件在生成pyd的过程中是否有用,答案时否定的,你完全可以删除那个文件。当然python也可以通过ctype的方式使用dll中的函数,但这个过程需要给python定义特殊的类,使用起来十分麻烦。
开发python的三方库的工作量并不小,本文只是一种简单的介绍。如果想要了解更多,读者需要查询更多相关资料,阅读更多相关书籍。