Python與C進行混合編程

寫在前面

  閱讀這篇文章需要一定的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的三方庫的工作量並不小,本文只是一種簡單的介紹。如果想要了解更多,讀者需要查詢更多相關資料,閱讀更多相關書籍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章