从C API中的字符串表示创建对象



我正在研究一个嵌入Python解释器的系统,我需要从C API中构造一个给定字符串的PyObject*

我有一个代表字典的const char*,以适当的格式使eval()从Python中正常工作,即:"{'bar': 42, 'baz': 50}"

目前,这是通过使用Py_Unicode_ api(表示字符串)作为PyObject*传递给Python的,所以在我的Python解释器中,我可以成功地编写:

foo = eval(myObject.value)
print(foo['bar']) # prints 42

我想将其更改为自动"eval"C端的const char*,并返回一个代表完整字典的PyObject*。我怎么去把这个字符串转换成一个字典在C API?

有两种基本方法:

第一个是简单地调用eval,就像你在Python中做的那样。唯一的技巧是需要builtins模块的句柄,因为在C API中无法免费获得。有很多方法可以做到这一点,但一个非常简单的方法是直接导入它:

/* or PyEval_GetBuiltins() if you know you're at the interpreter's top level */
PyObject *builtins = PyImport_ImportModule("builtins");
PyObject *eval = PyObject_GetAttrString(builtins, "eval");
PyObject *args = Py_BuildValue("(s)", expression_as_c_string);
PyObject *result = PyObject_Call(eval, args);

(这是未经测试的代码,它至少泄漏了引用,如果你想在C端处理异常,它不会检查NULL返回……但它应该足以让你理解这个想法。)

这样做的一个好处是,您可以以与eval完全相同的方式使用ast.literal_eval(这意味着您可以获得一些免费验证);只需将"builtins"更改为"ast",将"eval"更改为"literal_eval"。但真正的好处是,你所做的正是eval在Python中所做的,你已经知道这正是你想要的。

另一种选择是使用编译api。在真正高级的地方,您可以从"foo = eval(%s)"PyRun_SimpleString中构建一个Python语句。在此之后,使用Py_CompileString解析和编译表达式(您也可以在单独的步骤中解析和编译,但这在这里没有用),然后使用PyEval_EvalCode在适当的全局变量和局部变量中对其求值。(如果您自己不跟踪全局变量,请使用解释器反射api PyEval_GetLocalsPyEval_GetGlobals。)注意,我给出的是每个函数的超简化版本;通常您希望使用兄弟函数中的一个。但是你可以很容易地在文档中找到它们

最新更新