目录

  • 写在前面
  • 在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的核心即是动态链接库。当你读完这篇文章时,你会对这句话有更深的体会。

Python中混合运算 python混合编程_Python开发者


  在配置中先选中所有配置,因为在最后发布之前应选择Debug模式,生成用Release生成你所需要的Python库

Python中混合运算 python混合编程_python_02

设置调整

  以下过程不分先后

step1 选择输出目录

  你可以选择一个目录作为输出目录,但不是绝对的,但必须确保,这个目录是能被Python正常访问的,把pyd文件生成在dlls目录下。

Python中混合运算 python混合编程_Python开发者_03


Python中混合运算 python混合编程_Python_04

step2 改变目标文件拓展名

  将目标文件扩展名改.pyd。.

Python中混合运算 python混合编程_Python的C++扩展_05

step3 添加附加目录

  在附加包含目录下导入Python的include文件,这个文件下包含了<Python.h>文件

Python中混合运算 python混合编程_python_06

step4 添加附加库目录

  在编译python库时,会需要调用到python的lib库

Python中混合运算 python混合编程_python_07


将这个目录添加到连接器->常规->目录

Python中混合运算 python混合编程_Python开发者_08

step5添加附加依赖项

  在链接器->输入->附加依赖项中添加对应的lib文件,教程中使用的是python3.6版本则导入python36.lib

Python中混合运算 python混合编程_Python开发者_09

知识储备

条件编译

  你很辛苦的开发了一个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的三方库的工作量并不小,本文只是一种简单的介绍。如果想要了解更多,读者需要查询更多相关资料,阅读更多相关书籍。