目录
- 写在前面
- 在vs中建立一个工程
-
- 设置调整
-
- step1 选择输出目录
- step2 改变目标文件拓展名
- step3 添加附加目录
- step4 添加附加库目录
- step5添加附加依赖项
- 知识储备
-
- 条件编译
- 简单例程
- 程序测试
- 补充说明
写在前面
阅读这篇文章需要一定的C/C++和Python基础,阅读完这篇文章,你将能够开发简单的Python的库。
笔者所使用的C++编译器是vs2017,所使用的Python版本是Python3.6-64。Python有Python2和Python3两个主流版本,而预计2020年,Python2可能就停止维护。这里仅介绍如何开发Python3库。如果你是参照文本来写Python库,请务必使用Python3。如果你有任何问题和建议,可以联系作者,707101557@qq.com。
在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
- 1
- 2
- 3
简单例程
通过这个例程你会学会用python生成一个简单的库,并调用这个库中生成的函数
; 例程中将生成一个返回值为正态分布的随机数,使用的算法是Box-Muller变换。这个算法的相关文献可以参考[https://blog.csdn.net/m0_37772174/article/details/81356434]
// 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;
}
- 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
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
程序测试
import first_pyd
first_pyd.RANDN(5)
- 1
- 2
补充说明
pyd是一种特殊的dll,但生成pyd文件的方式与传统dll文件,有读者问过dllmain.cpp文件在生成pyd的过程中是否有用,答案时否定的,你完全可以删除那个文件。当然python也可以通过ctype的方式使用dll中的函数,但这个过程需要给python定义特殊的类,使用起来十分麻烦。
开发python的三方库的工作量并不小,本文只是一种简单的介绍。如果想要了解更多,读者需要查询更多相关资料,阅读更多相关书籍。