首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何使用C++用pybind11调用python函数?

如何使用C++用pybind11调用python函数?
EN

Stack Overflow用户
提问于 2022-01-25 09:44:43
回答 1查看 1K关注 0票数 3

请考虑以下C++ pybind11程序:

代码语言:javascript
运行
复制
#include <pybind11/embed.h>

namespace py = pybind11;

int main() {
    py::scoped_interpreter guard{};

    py::dict locals;

    py::exec(R"(

        import sys

        def f():
            print(sys.version)

    )", py::globals(), locals);

    locals["f"]();  // <-- ERROR
}

py::exec调用和封闭的import sys调用都成功,但调用locals["f"]()引发异常:

代码语言:javascript
运行
复制
NameError: name 'sys' is not defined

在函数的第一行f上。

预期的行为是程序打印python系统版本。

有什么想法吗?

更新:

我按照@DavidW的建议修改了程序:

代码语言:javascript
运行
复制
#include <pybind11/embed.h>

namespace py = pybind11;

int main() {
    py::scoped_interpreter guard{};

    py::dict globals = py::globals();

    py::exec(R"(

        import sys

        def f():
            print(sys.version)

    )", globals, globals);

    globals["f"]();  // <-- WORKS NOW
}

现在起作用了。

我不能百分之百确定我明白发生了什么,所以我希望得到一个解释。

(特别是,对通用globals / locals字典的修改是否会影响任何其他脚本。是否有一些全局字典是exec脚本正在修改的python解释器的一部分?或者py::globals()是否获取该状态的副本,以便将已执行的脚本与其他脚本隔离?)

更新2

因此,全局和局部变量相同的字典是默认状态:

代码语言:javascript
运行
复制
$ python
>>> globals() == locals()
True
>>> from __main__ import __dict__ as x
>>> x == globals()
True
>>> x == locals()
True

...and表示两者的默认值是__main__.__dict__,不管是什么(__main__.__dict__py::globals()返回的字典)。

我仍然不清楚__main__.__dict__到底是什么。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-01-26 20:05:56

因此,最初的问题(在注释中得到了解决)是,具有不同的全局值和局部变量使得计算结果就像在类中一样(参见exec -- PyBind11函数的行为基本相同):

请记住,在模块级别上,全局词和局部变量是相同的字典。如果exec以全局和局部变量的形式获得两个单独的对象,则代码将被执行,就像它嵌入到类定义中一样。

函数作用域不查找其封闭类中定义的变量--这是行不通的。

代码语言:javascript
运行
复制
class C:
    import sys
    def f():
        print(sys.version)
        # but C.sys.version would work

因此,您的代码无法工作。

pybind11::globals返回一个字典在很多地方都有

返回表示当前执行框架中的全局变量的字典,如果没有框架(通常是在嵌入解释器时),则返回__main__.__dict__

因此,对本词典的任何修改都将是持久的,并且保持不变(这可能不是您想要的!)在您的例子中,它可能是__main__.__dict__,但通常“当前执行框架”可能会根据跨越C++-Python边界的程度而改变。例如,如果一个Python函数调用一个修改globals()globals()函数,那么您所修改的内容完全取决于调用者。

我的建议是创建一个新的空dict,并将其传递给exec。这确保您在一个新的、非共享的命名空间中运行。

__main__只是一个表示“顶级代码环境”的特殊模块。就像任何模块都有一个__dict__。在REPL中运行时,它是全局范围。从pybind11的角度来看,它只是一个带有dict的模块,您可能不应该随意地将它写入其中(除非您真的决定有意地将一些东西放在那里,以便在全球共享它)。

关于__builtins__:Python exec函数的文档如下

如果全局字典不包含键__builtins__的值,则在该键下插入对内置模块内置字典的引用。这样,您就可以通过将自己的__builtins__字典插入全局,然后将其传递给exec(),来控制执行的代码可以使用哪些内置程序。

看看PyRun_String的代码,Pybind11 exec调用的代码,同样适用于这里。

这本词典似乎足以正确查找内建函数。(如果不是这样的话,那么您可以始终使用pybind11::dict(pybind11::module::import("builtins").attr("__dict__"))来复制内置数据集并使用它。但是,我不认为这是必要的

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70846440

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档