C或C++调用python一般都可以使用python自带的库完成,首先添加python.h的头文件,链接时加上对应的库即可。
但是在C/C++程序中使用到python的类时会遇到很多坑,网上搜会得到很多相似的方法如下所示
首先声明,以下的方法是有问题的,有问题的地方我会使用红字标出
注: 下述所有导入方法在导入失败时不会报错, 只会返回空指针.
第一步是导入.py文件:
- 使用PyObject* pModule来存储导入的.py文件模块, 调用的方法是PyImport_ImportModule(path): PyObject* pModule = PyImport_ImportModule("testpy");
- 使用PyObject* pDict来存储导入模块中的方法字典, 调用的方法是PyModule_GetDict(module): PyObject* pDict = PyModule_GetDict(pModule);
这样就完成了.py文件的导入.
第二步是导入已导入模块中的方法或类:
- 获取方法, 调用的方法是PyDict_GetItemString(dict, methodName): PyObject* pFunHi = PyDict_GetItemString(pDict, "sayhi");
- 获取类, 调用的方法同上, 注意红体部分的字符串对应于.py文件中的类/方法名: PyObject* pClassSecond = PyDict_GetItemString(pDict,"Second");
第三步是使用导入的方法或类:
- 使用方法, 调用PyObject_CallFunction(pFunc, "s", args)即可: PyObject_CallFunction(pFunHi, "s", "lhb");
- 使用类构造对象, 调用PyInstance_New(pClass, NULL, NULL)即可: PyObject* pInstanceSecond = PyInstance_New(pClassSecond, NULL, NULL); , 注意其中的pClassSecond为第二步.2中获取的类指针
- 使用类对象的方法, 调用PyObject_CallMethod(pInstance, methodname, "O", args)即可: PyObject_CallMethod(pInstanceSecond,"invoke", "O", pInstancePerson);
- 上述调用中的"s"和"O"代表的是参数列表的类型, 我们可以在 Py_BuildValue 找到所有的类型, 本文最后也附了此表.
PyInstance_New是python2使用的函数,python3则使用新的函数PyInstanceMethod_New,国内很多文章都认为这两个函数返回的是类的实例对象,其实不然,它们返回的是该类的构造函数对象。
按照上面文章的步骤做在不涉及修改类的成员变量时是没有问题的,如果有python类
class Test:
def __init__(self):
self.i = 1
print("init!")
def modify(self):
self.i+=1
def do(self):
print(self.i)
如果使用上面的方法使用这个类,调用do方法是不会有问题的,可以成功打印,但是如果调用modify则会报错,PyErr_Print打印错误信息则会提示i不存在。
并且在PyInstance_New或PyInstanceMethod_New时并不会打印init!,说明构造函数根本没有被调用。
正确的调用方式则应该是
PyObject* pConstruct = PyInstanceMethod_New(pClass);
PyObject* pIns = PyObject_CallObject(pConstruct,nullptr);
PyObject_CallMethod(pIns,"modify", nullptr);
PyObject_CallMethod(pIns,"do", nullptr);
使用PyInstanceMethod_New获得构造函数后才能构造对象,并且调用时不需要传递自身。