目錄
寫在前面
閱讀這篇文章需要一定的C/C++和Python基礎,閱讀完這篇文章,你將能夠開發簡單的Python的庫。
筆者所使用的C++編譯器是vs2017,所使用的Python版本是Python3.6-64。Python有Python2和Python3兩個主流版本,而預計2020年,Python2可能就停止維護。這裏僅介紹如何開發Python3庫。如果你是參照文本來寫Python庫,請務必使用Python3。如果你有任何問題和建議,可以聯繫作者,[email protected]。
在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變換。這個算法的相關文獻可以參考[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;
}
程序測試
import first_pyd
first_pyd.RANDN(5)
補充說明
pyd是一種特殊的dll,但生成pyd文件的方式與傳統dll文件,有讀者問過dllmain.cpp文件在生成pyd的過程中是否有用,答案時否定的,你完全可以刪除那個文件。當然python也可以通過ctype的方式使用dll中的函數,但這個過程需要給python定義特殊的類,使用起來十分麻煩。
開發python的三方庫的工作量並不小,本文只是一種簡單的介紹。如果想要了解更多,讀者需要查詢更多相關資料,閱讀更多相關書籍。