Python/C 相互调用

使用C语言扩展Python

声明接口

#pragma once

#define DLL_API	__declspec(dllexport)
// #define CALL	__stdcall
#define CALL

DLL_API double CALL  py_get_squre(double);

使用C语言实现可被Python的直接调用的接口

#include <Python.h>
#pragma comment(lib,"python38.lib")

#include "calc.h"

/*
	实现相关功能的C函数
 */
DLL_API double CALL  py_get_squre(double a)
{
	return a * a;
}

/*
	【包裹函数】
	将python接口参数转换为C接口参数,调用C函数,并将返回的C参数转换为python参数返回
	过程:传入Python参数 -> 转换为C参数 -> 调用C接口 -> 返回C参数 -> 转换为python参数返回
*/
static PyObject * _py_get_squre(PyObject * self, PyObject * args)
{
	double _a;
	double ret;

	if (!PyArg_ParseTuple(args, "d", &_a))
	{
		printf("PyArg_ParseTuple 参数解析失败\n");
		return NULL;
	}
	ret = py_get_squre(_a);
	return PyFloat_FromDouble(ret);
}

/*
	Method导出表 calc_module_methods(名字自己随便起)
	告诉python模块里有哪些参数可以被调用
*/
static PyMethodDef calc_module_methods[] = 
{
	{
		"py_get_squre",			//给出python环境的函数名称
		_py_get_squre,			//包裹函数
		METH_VARARGS,			//该宏表示:参数变长
		""						//一个说明性的字符串
	},

	// 导出表以 ... 结尾
	{NULL, NULL, 0, NULL}
};

static struct PyModuleDef calc_module = 
{
	PyModuleDef_HEAD_INIT,
	"calc_module",
	NULL,
	-1,
	calc_module_methods
};

/*
	导出函数 PyInit_calc_module(名字不能随便起,"PyInit_ + 模块名")
	导出函数中将模块名称与导出表进行连接
*/
PyMODINIT_FUNC PyInit_calc(void)
{
	PyObject *m;
	m = PyModule_Create(&calc_module);
	if (m == NULL)
	{
		printf("模块加载失败\n");
		return NULL;
	}
	// Debug
	//printf("PyInit calc_module\n");
	return m;
}

在C中调用Python脚本

def factorial(n):
    if n < 0:
        print("参数输入错误\n")
        return False
    if n == 0:
        print(1)
        return True
    ret = 1
    for i in range(1, n+1):
        ret *= i
    print(ret)

    return True
/*
 * @breaf VS2017 C调用Python脚本
 *
 * 问题记录:
 * 1.报错:python37_d.lib 在Debug模式下,需要在安装python的时候安装相应的"python debug bin/library"
 *        有对应的选项,具体名称我记不住了。安装的时候如果忘记安装了,后续Modify也可以
 */
#include <Python.h>
/*
	C调用Python模块实现的接口
*/
int factorial(int n)
{
	PyObject * pModule, *pFun;
	PyObject *pArgs, *pValue;
	PyObject * pModuleName;

	/*
		默认情况,需要将该python脚本放在输出的exe文件目录下,才能正常被导入。
		若添加上这句,表示导入当前目录下的python脚本,即可以把脚本放在工程目录下
		方便Debug和Release调用。也方便修改python脚本
		(当然了也可以写为绝对地址)

		PyRun_SimpleString("import sys");
		PyRun_SimpleString("sys.path.append('F:/Code/VS2017/pyc/pyc')");
	*/
	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('./')");

	// 导入模块,即自己写的python脚本
	pModuleName = PyUnicode_FromString("arith");
	pModule = PyImport_Import(pModuleName);
	// pModule = PyImport_ImportModule("Apaki");
	if (pModule == NULL)
	{
		printf("导入python模块失败\n");
		return 0;
	}
	// 确定调用的函数名称
	pFun = PyObject_GetAttrString(pModule, "factorial");

	// 配置传递的参数
	pArgs = PyTuple_New(1);
	PyTuple_SetItem(pArgs, 0, PyLong_FromLong(n));

	// 调用函数,并且取得返回值
	pValue = PyObject_CallObject(pFun, pArgs);

	if (pValue != Py_True)
	{
		printf("返回结果无效\n");
		return 0;
	}
	// 若返回0,则失败,返回1数据正常
	return 1;
}