【问题标题】:Calling a function of an object instance using embedded Python使用嵌入式 Python 调用对象实例的函数
【发布时间】:2019-02-05 02:24:15
【问题描述】:

我希望能够在我的应用程序中运行 Python 脚本,以允许自动化处理和修改现有对象/现有对象的调用方法。

在我的应用程序中有一个派生自前者的BasicWindow 类和MainWindow 类。现在在应用程序启动时,我初始化MainWindow 的一个实例。这个对象有很多功能,其中有一个是加载文件(LoadFile()),这里我就以它为例。

假设我想调用该特定对象实例的特定函数(但不限于该函数,它只是我想从 Python 实现的功能的一个示例)。

此方法不是静态方法。为此,我使用 Boost.Python 并以这种方式创建模块:

BOOST_PYTHON_MODULE(MyModule)
{

    MainWindow::PythonExpose(); //not really sure how to operate here
    //more stuff
}

我的想法是我可以从 Python 调用类似的东西:

MainWindow.LoadFile()

甚至更好,只是:

LoadFile()

一种解决方案可能是创建静态的、应用程序范围的函数,然后只公开这些函数。在 C++ 中,我可以找到 MainWindow 的特定实例:(两种方法都是静态的)

void AppHelper::LoadFile()
{
    GetMainWindow()->LoadFile();
}


void AppHelper::PythonExposeGlobal()
{
    using namespace boost::python;
    def("LoadFile", &AppHelper::LoadFile);
}

有可能实现吗?一般的问题是:是否可以从 Python 调用现有对象的方法(在 C++ 中)?如果是这样,该怎么做?如果没有,我可以做些什么来模仿这种行为?

例如,我可以轻松地在我的 C# 应用程序中启用脚本功能并共享现有对象的实例。 (当然 C# 有反射)。

【问题讨论】:

  • 是的,无论是你提议的方式,还是你公开类(没有构造函数,只是你需要的函数),创建一个引用现有实例的 python 对象(使用指针 -- 例如bp::object(bp::ptr(&foo)) ) 然后将其推送到解释器,以便您的脚本可以使用它。

标签: python c++ c++11 boost


【解决方案1】:

如果您可以保证该对象将在任何使用它的脚本运行时一直存在,那么我使用了一种相当简单的方法。

我将使用一个原始的计数器类进行演示:

class counter
{
public:
    counter() : count(0) {}

    void increment() { ++count; }

    int count;
};

现在,我将这个类暴露给 python,这样它就认为它是不可复制的,并且不允许构造新实例。我还公开了我想从脚本中使用的任何成员。

BOOST_PYTHON_MODULE(example)
{
    bp::class_<counter, boost::noncopyable>("Counter", bp::no_init)
        .def("increment", &counter::increment)
        ;
}

下一步是创建一个使用现有实例的 Python 对象,并允许脚本使用它(例如,将其添加为某个模块的属性,例如主模块)。

counter c;

bp::object main_module(bp::import("__main__"));

main_module.attr("c") = bp::object(bp::ptr(&c));

现在你的脚本可以使用这个实例了:

c.increment()

示例程序:

#include <boost/python.hpp>
#include <iostream>

namespace bp = boost::python;

// Simple counter that can be incremented
class counter
{
public:
    counter() : count(0) {}

    void increment() { ++count; }

    int count;
};

// Expose the counter class to Python
// We don't need constructor, since we only intend to use instance
// already existing on the C++ side
BOOST_PYTHON_MODULE(example)
{
    bp::class_<counter, boost::noncopyable>("Counter", bp::no_init)
        .def("increment", &counter::increment)
        ;
}

int main()
{
    Py_InitializeEx(0);

    // Bind our class
    initexample();

    counter c;

    bp::object main_module(bp::import("__main__"));
    bp::object main_namespace(main_module.attr("__dict__"));

    // Add the current instance of counter to Python as attribute c of the main module
    main_module.attr("c") = bp::object(bp::ptr(&c));

    std::cout << "Before: " <<  c.count << '\n';

    // Increment the counter from Python side
    bp::exec("c.increment()", main_namespace);

    std::cout << "After: " << c.count << '\n';

    Py_Finalize();
    return 0;
}

控制台输出:

Before: 0
After: 1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-13
    • 1970-01-01
    • 2022-06-19
    • 2014-12-03
    • 1970-01-01
    • 2013-07-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多