从C++调用 Python 类方法,如果给定一个初始化的类作为 PyObject

Calling a Python class method from C++, if given an initialised class as PyObject

本文关键字:初始化 一个 PyObject Python 调用 C++ 类方法 如果      更新时间:2023-10-16

我在 c++ 中有一个函数,它接收一个初始化的类作为 PyObject。 python类是:

class Expression:
def __init__(self, obj):
self.obj = obj
def get_source(self):
#Check if the object whose source is being obtained is a function.
if inspect.isfunction(self.obj):
source = inspect.getsourcelines(self.obj)[0][1:]
ls = len(source[0]) - len(source[0].lstrip())
source = [line[ls:] for line in source]
#get rid of comments from the source
source = [item for item in source if item.lstrip()[0] != '#']
source = ''.join(source)
return source
else:
raise Exception("Expression object is not a function.")

c++ 接收以下内容:

Expression(somefunctogetsource)

从 c++ 如何调用表达式对象的 get_source 方法? 到目前为止,我已经阅读了python c-api文档并尝试了这样的事情:

PyObject* baseClass = (PyObject*)expression->ob_type;
PyObject* func = PyObject_GetAttrString(baseClass, "get_source");
PyObject* result = PyObject_CallFunctionObjArgs(func, expression, NULL);

并将结果转换为字符串,但这不起作用。

比你做的更简单。无需直接从基类中检索任何内容。只需做:

PyObject* result = PyObject_CallMethod(expression, "get_source", NULL);
if (result == NULL) {
// Exception occurred, return your own failure status here
}
// result is a PyObject* (in this case, it should be a PyUnicode_Object)

PyObject_CallMethod采用一个对象来调用方法,一个 C 样式字符串作为方法名称,一个格式字符串 + varargs 作为参数。当不需要参数时,可以NULL格式字符串。

生成的PyObject*对于C++代码不是非常有用(它运行时确定了 1、2 或 4 个字节字符,具体取决于所涉及的序数,因此从它直接将内存复制到std::stringstd::wstring将不起作用),但PyUnicode_AsUTF8AndSize可用于获取 UTF-8 编码版本和长度,可用于有效地构建具有等效数据的std::string

如果性能很重要,您可能希望在模块加载期间显式创建一个表示"get_source"PyObject*,例如使用全局如下:

PyObject *get_source_name;

在模块的PyMODINIT_FUNC中初始化为:

get_source_name = PyUnicode_InternFromString("get_source");

一旦有了它,您就可以将更有效的PyObject_CallMethodObjArgs用于:

PyObject* result = PyObject_CallMethodObjArgs(expression, get_source_name, NULL);

节省的主要原因是避免一遍又一遍地从 Cchar*构造 Python 级别的str,并且通过使用PyUnicode_InternFromString构造字符串,您可以使用暂留的字符串,使查找更有效(因为当在解释器中def-ed 时,get_source的名称本身会自动扣留, 不会对内容进行实际内存比较;它意识到这两个字符串都被扣留了,并且只检查它们是否指向相同的内存)。