首页 > 其他分享 >pybind11内嵌解释器

pybind11内嵌解释器

时间:2023-06-24 22:12:19浏览次数:42  
标签:内嵌 解释器 Python py module pybind11 interpreter

一、创建解释器

需要在使用任意Python API前初始化解释器,包括pybind11 Python函数和类。RAII guard类`scoped_interpreter`可用来管理解释器的生命周期。在guard类销毁时,解释器将会关闭并占用的内存。必须在所有Python函数前调用它。

#include <pybind11/embed.h> // everything needed for embedding
namespace py = pybind11;

int main() {
    py::scoped_interpreter guard{}; // start the interpreter and keep it alive

    py::print("Hello, World!"); // use the Python API
}

也可以使用pybind11 API来实现相同的功能:(参见PythonC++接口)

#include <pybind11/embed.h>
namespace py = pybind11;

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

    py::exec(R"(
        kwargs = dict(name="World", number=42)
        message = "Hello, {name}! The answer is {number}".format(**kwargs)
        print(message)
    )");
}
#include <pybind11/embed.h>
namespace py = pybind11;
using namespace py::literals;

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

    auto kwargs = py::dict("name"_a="World", "number"_a=42);
    auto message = "Hello, {name}! The answer is {number}"_s.format(**kwargs);
    py::print(message);
}

两种方法也可以混合使用:

#include <pybind11/embed.h>
#include <iostream>

namespace py = pybind11;
using namespace py::literals;

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

    auto locals = py::dict("name"_a="World", "number"_a=42);
    py::exec(R"(
        message = "Hello, {name}! The answer is {number}".format(**locals())
    )", py::globals(), locals);

    auto message = locals["message"].cast<std::string>();
    std::cout << message;
}

 二、导入模块

使用`module_::import()`可以导入Python模块。

py::module_ sys = py::module_::import("sys");
py::print(sys.attr("path"));

为方便起见,内嵌解释器时,会将当前工作路径包含到`sys.path`中。这样我们可以方便地导入本地Python文件。

```python
"""calc.py located in the working directory"""


def add(i, j):
    return i + j
```

```c++
py::module_ calc = py::module_::import("calc");
py::object result = calc.attr("add")(1, 2);
int n = result.cast<int>();
assert(n == 3);
```

如果运行时源文件被修改(如被外部进程修改),可以使用`module_::reload()`重新导入模块。这在下面的场景中十分有用:有个应用程序要导入用户定义数据处理脚本,该脚本需要在用户修改后更新时。注意,这个函数不会递归地重新加载模块。

三、添加内嵌模块

使用宏`PYBIND11_EMBEDDED_MODULE`可以添加内嵌的二进制模块。这个定义需要放在全局作用域中。定义后,他们可以向其他模块一样导入。

#include <pybind11/embed.h>
namespace py = pybind11;

PYBIND11_EMBEDDED_MODULE(fast_calc, m) {
    // `m` is a `py::module_` which is used to bind functions and classes
    m.def("add", [](int i, int j) {
        return i + j;
    });
}

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

    auto fast_calc = py::module_::import("fast_calc");
    auto result = fast_calc.attr("add")(1, 2).cast<int>();
    assert(result == 3);
}

与只能创建单个二进制模块的扩展模块不同,在嵌入式方面,可以使用多个“PYBIND11_embedded_module”定义添加无限数量的模块(只要它们具有唯一的名称)。这些模块被添加到Python的内置列表中,因此它们也可以导入到解释器加载的纯Python文件中。一切自然互动:

"""py_module.py located in the working directory"""
import cpp_module

a = cpp_module.a
b = a + 1
#include <pybind11/embed.h>
namespace py = pybind11;

PYBIND11_EMBEDDED_MODULE(cpp_module, m) {
    m.attr("a") = 1;
}

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

    auto py_module = py::module_::import("py_module");

    auto locals = py::dict("fmt"_a="{} + {} = {}", **py_module.attr("__dict__"));
    assert(locals["a"].cast<int>() == 1);
    assert(locals["b"].cast<int>() == 2);

    py::exec(R"(
        c = a + b
        message = fmt.format(a, b, c)
    )", py::globals(), locals);

    assert(locals["c"].cast<int>() == 3);
    assert(locals["message"].cast<std::string>() == "1 + 2 = 3");
}

四、解释器的生命周期

当 `scoped_interpreter` 销毁时,程序会自动关闭Python解释器。后面再创建一个新的示例会重启解释器。或者,我们也可以使用 `initialize_interpreter` / `finalize_interpreter` 这组函数在任意时刻直接设置解释器状态。

解释器重启后,pybind11创建的模块可以安全地重新初始化,但第三方扩展模块可能会有些问题。问题在于Python本身不能完全卸载扩展模块,并且会有一些解释器重启的警告。简而言之,由于Python引用循环或用户创建的全局数据,并非所有内存都可能被释放。具体细节可以查看CPython文档。

标签:内嵌,解释器,Python,py,module,pybind11,interpreter
From: https://www.cnblogs.com/okmai77xue/p/17501750.html

相关文章

  • Python潮流周刊#8:Python 3.13 计划将解释器提速 50%!
    你好,我是猫哥。这里每周分享优质的Python及通用技术内容,部分为英文,已在小标题注明。(标题取自其中一则分享,不代表全部内容都是该主题,特此声明。)首发于我的博客:https://pythoncat.top/posts/2023-06-24-weekly......
  • pybind11绑定类(一)
    一、自定义数据结构-结构体`class_`会创建C++class或struct的绑定。`init()`方法使用类构造函数的参数类型作为模板参数,并包装相应的构造函数;静态成员函数需要使用`class_::def_static`来绑定#include<pybind11/pybind11.h>namespacepy=pybind11;structPet{Pet......
  • 20230430 27. 解释器模式 - 音符
    解释器模式(interpreter),给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。解释器模式需要解决的是,如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器......
  • C 语言 GCC 内嵌函数实现 Lambda 表达式
    代码({//函数实现函数名称;})#include<stdio.h>#include<malloc.h>#defineaction_lambda(function_body)\({voidlambda_funcfunction_bodylambda_func;})#definefunc_lambda(return_type,function_body)\({return_typelambda_funcfunction_b......
  • linux中进入python交互解释器Tab补全功能
    进入python交互解释器后,按tab键默认是缩进功能,而不是代码补全。为了实现代码补全,可以采用如下操作:1、创建指令补全文件[root@room8pc16 ~]# vim /usr/local/bin/tab.pyfrom rlcompleter import readlinereadline.parse_and_bind('tab: complete')2、配置环境变量,在~/.bashrc......
  • [pybind11]为c++项目写python API接口
    C++项目的pybind方法有哪些?有什么区别?以下是主要的python绑定cpp的方法:方法年份代表用户适用于CPython的C/C++扩展模块1991标准库PyBind11(推荐用于C++)2015Cython(推荐用于C)2007gevent、kivyHPy2019mypyc2017ctype2003oscryptocffi......
  • pybind11基本用法
    1、头文件和命名空间约定#include<pybind11/pybind11.h>namespacepy=pybind11;2、函数绑定`PYBIND11_MODULE`会创建一个函数,它在Python中使用`import`语句时被调用。宏的第一个参数是模块名(example),不使用引号包住;第二个参数是类型为`py::module_`的变量(m),它是创建绑定的......
  • Python设计模式-21-解释器模式
    解释器模式是一种行为型设计模式,它定义了一种语言,用于解释和执行特定的任务。解释器模式通常包括以下几个角色:抽象表达式(AbstractExpression):定义了一个接口,用于解释和执行特定的任务。终结符表达式(TerminalExpression):实现了抽象表达式定义的接口,并表示语言中的终结符。......
  • java开发C语言解释器:数组元素的读取和赋值
    本节技术内容难度较大,请结合视频对代码的讲解和调试来理解本节内容:用java开发编译器一个成熟的编译器或解释器,要能够解析和执行目标语言开发的逻辑复杂的程序代码,我们用java开发的C语言解释器,能够执行用C语言开发的较为复杂的程序时,才称得上是合格的,从本节开始,我们致力于C语言解......
  • 如何安装安装Python解释器
    安装Python解释器的步骤如下:打开Python官网:https://www.python.org/downloads/,下载最新的Python版本(例如Python3.11.4)。双击下载的安装程序,按照提示一步步进行安装;在安装过程中,要注意勾选“AddPython3.xtoPATH”选项,这样就可以在命令行中直接使用Python命令了。Win+......