繁体   English   中英

如何使用 pybind11 从 C++ 调用 python function?

[英]How to call a python function from C++ with pybind11?

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

#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"]()会抛出异常:

NameError: name 'sys' is not defined

在 function f的第一行。

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

有任何想法吗?

更新:

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

#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
}

现在它可以工作了。

我不是 100% 确定我理解发生了什么,所以我会很感激解释。

(特别是通用globals / locals字典的修改是否会影响任何其他脚本。是否有一些全局字典是exec脚本正在修改的 python 解释器的一部分?或者py::globals()是否复制了它state 所以执行的脚本与其他脚本隔离?)

更新 2

所以看起来有全局变量和局部变量是同一个字典是默认的 state:

$ python
>>> globals() == locals()
True
>>> from __main__ import __dict__ as x
>>> x == globals()
True
>>> x == locals()
True

...并且两者的默认值是__main__.__dict__ ,无论是什么( __main__.__dict__py::globals()返回的字典)

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

所以最初的问题(在评论中解决)是具有不同的全局变量和局部变量会导致它被评估为好像它在 class 中(参见Python 文档的exec - PyBind11 ZC1C425268E68384D1AB450 的行为基本相同)

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

A function scope 不查找在其封闭的 class 中定义的变量 - 这不起作用

class C:
    import sys
    def f():
        print(sys.version)
        # but C.sys.version would work

因此您的代码不起作用。


pybind11::globals返回一个在多个地方共享的字典:

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

因此对这本字典的任何修改都将是持久的并保持不变(这可能不是你想要的。)。 在您的情况下,它可能是__main__.__dict__但通常“当前执行框架”可能会从调用到调用发生变化,具体取决于您跨越 C++-Python 边界的程度。 For example, if a Python function calls a C++ function that modifies globals() then exactly what you modify depends on the caller.

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


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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM