C++使用Python/C API

目录

配置环境

第一步 导入Python3路径

第二步 测试环境

 运行例程

1. 直接运行Python代码段

2. 运行Python脚本

3. 导入Python模块,调用Python函数

4. 调用Python类

参考资料


配置环境

环境:Windows10、Visual Studio 2019、Python3.x

打开VS,建立空项目。

第一步 导入Python3路径

1. 项目→属性→C/C++→常规→附加包含目录,添加Python3安装路径\include

该文件夹下有Python.h,这就是C++调用Python的API前一定要include的库。

 2.  项目→属性→VC++目录→库目录,添加Python3安装路径\libs

 3. 项目→属性→链接器→输入→附加依赖项,添加Python3安装路径\libs\python39.lib。(如果Python版本是3.7则可以写python37.lib,以此类推)

4. 检查版本: 检查一下自己下载的Python是x86还是x64版本的,如果与VS版本不一致,可以在VS窗口上方调整项目版本。

第二步 测试环境

这是一个简单的helloworld程序。如果能够成功运行,则可以直接进入下一节。

#include <Python.h>

int main(int argc, char* argv[])
{
	Py_Initialize();
	PyRun_SimpleString("print('Hello, world!')");
	Py_Finalize();
	return 0;
}

 运行结果:

如果出现错误:LINK : fatal error LNK1104: 无法打开文件“python39_d.lib”,则说明没有安装Python的Debug功能,需要为Python增加Debug功能。增加方法如下图:

(注:针对该错误,除了本文中提到的方法,网上还有其它办法可供选择:扩展Python模块系列(一)----开发环境配置 - 建木 - 博客园 (cnblogs.com)。)

 运行例程

C++调用Python的结构都如下:

#include <Python.h>

int main(int argc, char* argv[])
{
	Py_Initialize(); //初始化

    //... ...

	Py_Finalize(); //结束,释放资源

	return 0;
}

注:下文仅包含C++运行或调用Python代码段、脚本、函数、类的基本方法,不包含进阶用法。

考虑到便利性,也由于其在 Python 解释器中被广泛使用, "Python.h" 还包含了一些标准头文件: <stdio.h> , <string.h> , <errno.h> 和 <stdlib.h> 。 如果后面的头文件在你的系统上不存在,它还会直接声明函数 malloc () , free () 和 realloc () 。

——Python官方文档

1. 直接运行Python代码段

#include <Python.h>

int main(int argc, char* argv[])
{
	Py_Initialize(); 

	PyRun_SimpleString("print('Hello from Python string')");

	Py_Finalize(); 
	return 0;
}

2. 运行Python脚本

源目录下helloScript.py:

print("Hello from Python script")

C++:

//my_python.c
#include <Python.h>

int main(int argc, char* argv[])
{
	Py_Initialize(); 

	PyObject* obj = Py_BuildValue("s", "helloScript.py");
	FILE* file = _Py_fopen_obj(obj, "r+");
	if (file != NULL)
	{
		PyRun_SimpleFile(file, "helloScript.py");
	}

	Py_Finalize(); 
	return 0;
}

3. 导入Python模块,调用Python函数

源目录下hello.py:

def Hello():
    print("Hello from Python module")

def Add(a, b):
    print ("Add function: ", end="")
    print (str(a) + " + " + str(b) + " = " + str(a+b))
    return a + b

C++:

#include <Python.h>

int main(int argc, char* argv[])
{
	Py_Initialize(); 

	//必须修改Python路径,否则会找不到Python模块
    PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('./')");

	PyObject* pModule = NULL;
	PyObject* pFunc = NULL;

    //import模块
	pModule = PyImport_ImportModule("hello");//模块文件名
    //找不到模块则报错
	if (pModule == nullptr) {
		PyErr_Print();
		Py_Finalize();
		return 0;
	}

    //1. 调用不带参数的函数 Hello()
	pFunc = PyObject_GetAttrString(pModule, "Hello");//函数名
	PyObject_CallFunction(pFunc, NULL);//调用函数

	//2. 调用带参数的函数 Add(a, b)
    PyObject* pAdd = NULL;
	PyObject* args = NULL;
	pAdd = PyObject_GetAttrString(pModule, "Add");
	args = Py_BuildValue("(ii)", 123, 456); //设置传入Add的参数
	PyObject* pRet = PyObject_CallObject(pAdd, args); //pRet = Add(123, 456)

	//输出返回值
    int ans = 0;
	PyArg_Parse(pRet, "i", &ans);//返回类型转换
	printf("Return C++: ans = %d\n", ans);

	Py_Finalize(); 

	return 0;
}

4. 调用Python类

注:调用类中方法的API不止一套,建议去官网看看,并且建议使用Python3.9版本以上才支持的API,那些API对类的支持更好,不容易出现需要手动加入Python类的self参数的问题。

源目录下helloClass.py:

class myClass:
    welcome = "Hello from Python class attribute"

    def hello(self):
        print("Hello from Python class method")

    def minus(self, a, b):
        print(str(a) + " - " + str(b) + " = " + str(a-b))
        return a-b

C++:

(1)Python3.9以下可用:

#include <Python.h>

int main(int argc, char* argv[])
{
	Py_Initialize(); 

	//必须修改Python路径,否则会找不到Python模块
    PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('./')");

	PyObject* pModule2 = NULL;
	PyObject* pFunc2 = NULL;
	PyObject* pClass = NULL;
	PyObject* pInstance = NULL;
	PyObject* args2 = NULL;

	pModule2 = PyImport_ImportModule("helloClass");//这里是要调用的文件名helloClass.py
	if (pModule2 == NULL)
	{
		PyErr_Print();
		Py_Finalize();
		return 0;
	}

	// 模块的字典列表
	PyObject* pDict = PyModule_GetDict(pModule2);
	if (!pDict) {
		PyErr_Print();
		Py_Finalize();
		return 0;
	}

	//从字典中获取myClass类
	PyObject* pClassCalc = PyDict_GetItemString(pDict, "myClass");
	if (!pClassCalc) {
		PyErr_Print();
		Py_Finalize();
		return 0;
	}

	//构造实例
	PyObject* pInstanceCalc = PyInstanceMethod_New(pClassCalc);
	if (!pInstanceCalc) {
		PyErr_Print();
		Py_Finalize();
		return 0;
	}

    //获取pInstanceCalc实例的属性,转换成字符串并输出
	PyObject* obj2 = PyObject_GetAttrString(pInstanceCalc, "welcome");
	PyObject* str = PyUnicode_AsEncodedString(obj2, "utf-8", "strict");
	char* result = PyBytes_AsString(str);
    printf("%s\n", result);

    //如果属性是int型,可用下面这句转换属性:
    //int qwq;
    //PyArg_Parse(obj2, "i", &qwq);

    //调用无参数Python方法,s和pInstanceCalc对应Python中的self
	PyObject_CallMethod(pClassCalc, "hello", "s", pInstanceCalc);
	
    //调用多参数Python方法,(s,i,i)和pInstanceCalc、12、22 分别对应Python方法中的 self、a、b
    pRet = PyObject_CallMethod(pClassCalc, "minus","(s,i,i)",pInstanceCalc, 12, 22);
	if (!pRet)
	{
		PyErr_Print();
		Py_Finalize();
		return 0;
	}

	int res = 0;
	PyArg_Parse(pRet, "i", &res);//转换返回类型

	printf("Return C++: ans = %d\n", res);


	Py_Finalize(); 

	return 0;
}

(2)Python3.9以上可用:

 

PyObject* getClass(const char* fileName, const char* className) {
	//必须修改Python路径,否则会找不到Python模块
	PyRun_SimpleString("import sys");
	PyRun_SimpleString("sys.path.append('./')");

	PyObject* pModule = NULL;
	PyObject* pClass = NULL;
	PyObject* pInstance = NULL;


	pModule = PyImport_ImportModule(fileName);
	if (pModule == NULL)
	{
		PyErr_Print();
		Py_Finalize();
		return NULL;
		//return 0;
	}

	// 模块的字典列表
	PyObject* pDict = PyModule_GetDict(pModule);
	if (!pDict) {
		PyErr_Print();
		Py_Finalize();
		return NULL;
		//return 0;
	}

	//从字典中获取myClass类
	pClass = PyDict_GetItemString(pDict, className);
	if (!pClass) {
		PyErr_Print();
		Py_Finalize();
		return NULL;
		//return 0;
	}
	//实例化
	pInstance = PyObject_CallObject(pClass, NULL);
	
	return pInstance;
}


int main(){
    Py_Initialize();

    PyObject* pInstance = getClass("sarsaScript", "RL");

	//调用多参数Python类方法
    //cjoose_action是方法名
	PyObject* pAction = PyUnicode_FromString("choose_action");
    std::string state = "state1";
	PyObject* pState = PyUnicode_FromString(state.c_str());
    
//参数以NULL结尾
	PyObject* pRet = PyObject_CallMethodObjArgs(pInstance, pAction, pState, NULL);
	if (!pRet)
	{
		PyErr_Print();
		Py_Finalize();
		return -1;
	}

	int res = 0;
	PyArg_Parse(pRet, "i", &res);//转换返回类型为int

    //调用无参数Python类方法
    //qwq是方法名
    PyObject* pQWQ = PyUnicode_FromString("qwq");
    PyObject_CallMethodNoArgs(pInstance, pQWQ);

	Py_Finalize();
	return 0;
}

把上面的四段代码整合到一起运行,控制台输出为:

参考资料

文档:

  1. 官方API文档The Very High Level Layer — Python 3.10.11 documentation
  2. API文档中文版本C++调用Python浅析_pyeval_callobject_magictong的博客-CSDN博客
  3. 官方使用教程1. 在其它应用程序嵌入 Python — Python 3.7.13 文档
  4. 官方API手册Python 中文文档 - Python/C API 参考手册 | Docs4dev

博客:

  1. 精炼:[Python]C用API与Python交互_cpython api_西北丰的博客-CSDN博客
  2. 初学:
    1. C++调用python脚本 - 知乎 (zhihu.com)
    2. [C++/Python] 如何在C++中使用一个Python类? (Use Python-defined class in C++) - Lancelod_Liu - 博客园 (cnblogs.com)
    3. c++调用python的代码、函数、类 - 简书 (jianshu.com)
  3. 细致:C语言扩展Python模块 - 随笔分类 - 建木 - 博客园 (cnblogs.com)
  4. 踩坑经验:这里要隆重的写一下C++调用python遇到的各种坑,搞了好久,终于成功了!_c++调用python initfsencoding: unable to load the fil_Darling100的博客-CSDN博客

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值