仅在对象实例的生命周期内保持 Python 解释器处于活动状态

Posted

技术标签:

【中文标题】仅在对象实例的生命周期内保持 Python 解释器处于活动状态【英文标题】:Keeping Python Interpreter Alive Only During the Life of an Object Instance 【发布时间】:2020-02-06 17:53:55 【问题描述】:

我已经定义了一个使用python解释器的类如下:

class pythonInt

public:
    pythonInt(const char* fname) 
        py::initialize_interpreter();
        m_Module = py::module::import(fname);
    
    ~pythonInt() 
        py::finalize_interpreter();
    
    py::module m_Module;
    // ... other class members and functions that uses m_Module
;

int main()

    pythonInt *p1 = new pythonInt("pybind_test1");
    delete(p1); 

    pythonInt *p2 = new pythonInt("pybind_test1");
    delete(p2); 

    return 0;

一旦类实例被破坏,当它到达删除实例_Py_Dealloc(op) 时,我会收到Access violation reading location 错误。如何完成解释器,以便成功删除之前创建的类实例p1 并安全地创建一个新的类实例p2

【问题讨论】:

【参考方案1】:

崩溃是b/c数据成员py::module m_Module;pythonInt的构造函数/析构函数运行之前创建并在运行之后销毁,因此在初始化之前和解释器完成之后。

pybind11 为您寻求的目的提供scoped_interpreter,C++ 保证访问块中所有数据成员的构造/销毁顺序。因此,假设您将所有(Python)数据保存在一起并且pythonInt 没有基类(带有 Python 数据成员),这将是一个选项:

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

namespace py = pybind11;
class pythonInt

public:
    pythonInt(const char* fname) 
        m_Module = py::module::import(fname);
    
    ~pythonInt() 
    
    py::scoped_interpreter m_guard;
    py::module m_Module;
    // ... other class members and functions that uses m_Module
;

int main()

    pythonInt *p1 = new pythonInt("pybind_test1");
    delete(p1);

    pythonInt *p2 = new pythonInt("pybind_test2");
    delete(p2);

    return 0;

与您的示例相比,它添加了#include &lt;pybind11/embed.h&gt;py::scoped_interpreter m_guard;(再次强调顺序至关重要);它删除了解释器的初始化/终结。

【讨论】:

谢谢维姆。代码成功通过delete(p1),这很棒。但是,它停在pythonInt *p2 = new pythonInt("pybind_test2"); 并给出error alreary set py::error_already_set 异常的 what() 打印出实际 Python 异常的 str()。那么,它完整地说明了什么?如果没有,请在语句周围使用try catch(...) 并通过PyErr_Print() 打印错误。 pybind_test1pybind_test2 模块中很可能存在 Python 错误。 我实际上将同一个文件传递给p1p2,它只在p2 上引发异常。我得到的错误是:Unhandled exception at 0x00007FFE27D0A839 in mypython_test.exe: Microsoft C++ exception: pybind11::error_already_set at memory location 0x0000009827F5F9B8。返回的对象PyObject *obj = PyImport_ImportModule(name);NULL 如果PyImport_ImportModule返回NULL,表示找不到模块。为了简化测试,请使用肯定存在的模块,例如。 sys. pybind_test1.py 或(在使用的模块中)看起来像引用计数问题。错误是 b/c 此谓词 (Py_REFCNT(type) != 0) 对于 PyStructSequence 类型失败。请注意,Python 解释器从不卸载共享库,即使此类库表示的扩展模块已完全删除,因此任何数据对象都会保留,并且显然引用计数没有正确减少。在pybind_test1.py 上进行一些增量调试,删除部分以查找有问题的代码,将允许精确定位有问题的代码(但您可能对此无能为力)。

以上是关于仅在对象实例的生命周期内保持 Python 解释器处于活动状态的主要内容,如果未能解决你的问题,请参考以下文章

如何创建仅在 JVM 生命周期内存在的 HSQLDB 数据库?

ECS 容器实例生命周期

Python 记忆/延迟查找属性装饰器

ViewState Vs Session ...通过页面生命周期维护对象

angularjs服务常见用法

故事板上对象的生命周期?