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

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

我在 C++ 中有一个函数接收初始化的 class 作为 PyObject。 python class 是:

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);

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

比你做的更简单。您不需要直接从基础 class 中检索任何内容。只是做:

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 风格的字符串作为方法名称,一个格式字符串 + 可变参数作为参数。当不需要参数时,格式字符串可以是 NULL.

结果 PyObject* 对 C++ 代码不是很有用(它具有运行时确定的 1、2 或 4 字节字符,具体取决于所涉及的序数,因此直接将内存从它复制到 std::stringstd::wstring 将不起作用),but PyUnicode_AsUTF8AndSize can be used to get a UTF-8 encoded version and length,可用于有效地构造具有等效数据的 std::string

如果性能很重要,您可能希望在模块加载期间明确地制作一个 PyObject* 表示 "get_source",例如全球喜欢:

PyObject *get_source_name;

在模块的 PyMODINIT_FUNC 中初始化为:

get_source_name = PyUnicode_InternFromString("get_source");

一旦你有了它,你就可以使用更高效的 PyObject_CallMethodObjArgs

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

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