首页 > 解决方案 > 如果给定一个初始化类为 PyObject,则从 C++ 调用 Python 类方法

问题描述

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

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

标签: pythonc++python-c-apic-api

解决方案


比你做的简单。您不需要直接从基类中检索任何内容。做就是了:

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不起作用),PyUnicode_AsUTF8AndSize可以用于获取UTF-8 编码的版本和长度,可用于有效地构造std::string具有等效数据的 a。

如果性能很重要,您可能希望在模块加载期间显式地进行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);

节省的主要是避免str从 Cchar*中一遍又一遍地构建 Python 级别,并且通过使用PyUnicode_InternFromString来构造字符串,您正在使用实习字符串,从而提高查找效率(因为 nameget_source本身在以下情况下会自动实习def- ed 在解释器中,不会对内容进行实际的内存比较;它意识到这两个字符串都是实习的,只是检查它们是否指向相同的内存)。


推荐阅读