将 Python 类实例传递给 C++ 函数
Posted
技术标签:
【中文标题】将 Python 类实例传递给 C++ 函数【英文标题】:Passing a Python class instance into a C++ function 【发布时间】:2014-05-03 04:18:30 【问题描述】:我有一个用 C++ 编写的内部库,我目前正在努力将其扩展到 Python。我在开始这项任务时考虑到了 Boost.Python,但我对替代方案持开放态度。
目前我有一个 C++ 函数需要接受一个 Python 类实例,然后使用这个对象的方法来执行某些任务。这个想法是让 Python 用户永远不需要处理 C++。他们应该从我将提供的 Python 模板/示例类创建这个 Python 对象,并使用我可以假设在我的 C++ 库中的预设方法名称。
暴露给 Python 用户的界面如下所示:
class Foo(object):
def __init__(self, args):
"""create an instance of this class with instance-specific attributes"""
def Bar1(self, a, b, c):
"""do something with the given integers a, b and c"""
pass
def Bar2(self, a, b, c):
"""do something else with the given integers a, b and c"""
pass
import mylib
cheese = mylib.Wine()
cheese.do_something(Foo)
在 C++ 中,对应的代码如下:
#include <boost/python.h>
#include <Python.h>
class Wine
public:
Wine() ;
~Wine() ;
static void do_something(boost::python::object *Foo)
int a = 1;
int b = 2;
int c = 3;
Foo->attr("Bar1")(a, b, c);
Foo->attr("Bar2")(a, b, c);
;
;
BOOST_PYTHON_MODULE(mylib)
using namespace boost::python;
class_<Wine>("Wine")
.def("do_something", &Wine::do_something);
;
我已经成功编译了这段代码,并验证了名为 Wine 的 C++ 类确实暴露给了 Python,并且我可以访问它的成员函数。如果我编写一个名为“greet()”的成员函数,它只返回“Hello, world!”,它就可以完美运行。
我需要在这里强调传递 Foo 实例的重要性。我没有奢侈地将 Foo 模块简单地导入 C++ 代码并在 C++ 中创建 Foo 的实例。我想从 Python 用户接收的对象具有我需要使用的属性,这些属性特定于实例,而不是类本身。
问题是我不知道如何将 Python 实例传递给 do_something,这样它就会在 C++ 中作为可调用的 boost::python::object 出现。上述代码返回如下 C++ 签名不匹配错误:
Boost.Python.ArgumentError: Python argument types in
Wine.do_something(Wine, Foo)
did not match C++ signature:
do_something(boost::python::api::object*)
两天在互联网上寻找答案没有取得任何进展。似乎有很多关于如何将 C++ 类传递给 Python 的信息,但我无法找到相反方向的信息。非常感谢这里的一些指导。
谢谢!
【问题讨论】:
【参考方案1】:初始代码有两个错误:
Boost.Python 正在尝试将两个参数(self
和Foo
的一个实例)传递给只接受一个参数的静态Wine::do_something()
C++ 函数。为了解决这个问题,在公开Wine
类时,Python 的Wine.do_something()
成员函数需要通过boost::python::class_::staticmethod()
成员函数设置为静态。当作为静态方法公开时,Boost.Python 将不再传递 self
实例参数。
与 Python/C API 不同,其中指针通常用作对象的句柄 (PyObject*
),Boost.Python 提供了更高级别的符号 boost::python::object
类,通常通过值或引用传递。在内部,该类与为PyObject
执行智能指针管理的boost::python::handle
交互。
这里是基于原代码的完整 Python 扩展:
#include <boost/python.hpp>
class Wine
public:
static void do_something(boost::python::object object)
int a = 1;
int b = 2;
int c = 3;
object.attr("Bar1")(a, b, c);
object.attr("Bar2")(a, b, c);
;
;
BOOST_PYTHON_MODULE(example)
namespace python = boost::python;
python::class_<Wine>("Wine")
.def("do_something", &Wine::do_something)
.staticmethod("do_something")
;
;
互动使用:
>>> class Foo(object):
... def Bar1(self, a, b, c):
... print "Bar1", locals()
... def Bar2(self, a, b, c):
... print "Bar2", locals()
...
>>> import example
>>> cheese = example.Wine()
>>> cheese.do_something(Foo())
Bar1 'a': 1, 'c': 3, 'b': 2, 'self': <__main__.Foo object at 0xb6b0f2ac>
Bar2 'a': 1, 'c': 3, 'b': 2, 'self': <__main__.Foo object at 0xb6b0f2ac>
【讨论】:
这非常有用。如果我的电脑回到前面,我会报告它的成功,但它肯定很有意义。一个问题。如果我理解正确,那么您的意思是 boost 对象类已经处理了指向 pyobject 的智能指针,因此接受对象而不是 object* 不会创建 Foo 的副本。如果是这样,那是个好消息,因为我们真的不想在这里克隆 Foo。它的目的是在实践中成为一个非常大的对象。 @AlpDener 很高兴它很有帮助。不会制作Foo
的副本。 Boost.Python 维护 Python 的参数传递语义,其中对象引用按值传递。有关生命周期和线程的更多信息(注意到您的其他评论提到了并行性),请考虑阅读this 答案。
我刚刚有机会在我的代码中实现这一点,我很高兴地报告它运行良好。还为您对线程的回复添加了书签,以防我们将来遇到这种情况。非常感谢!【参考方案2】:
要公开接受 Python 对象作为参数的方法,您应该使用 boost::python::object
而不是 boost::python::object *
void do_something(boost::python::object Foo)
要公开静态方法,请像普通函数一样公开它:def("do_something", Wine::do_something);
import mylib
# method
cheese = mylib.Wine()
foo = Foo()
cheese.do_something(foo)
#static method
foo = Foo()
mylib.do_something(foo)
【讨论】:
这会在 C++ 中复制 Foo 吗?这是非常不可取的。 Foo 对象旨在相当大(并且高度并行化)。我们希望 C++ 函数只引用这个 Python 对象。以上是关于将 Python 类实例传递给 C++ 函数的主要内容,如果未能解决你的问题,请参考以下文章