在C++中从局部作用域创建Python对象
我正在使用boost.python把两个C++类提供给Python使用。
class X {
public:
X();
}
class Y {
...
}
BOOST_PYTHON_MODULE(test) {
class_<X>("X", init<>());
class_<Y>("Y", init<>());
}
每当我在Python中创建一个新的X时,我希望能在C++中运行一些代码,这段代码会创建一个类型为Y的本地对象'y'。所以实际上,当我在Python中执行
x = X()
时,我希望这段代码也能运行
y = Y()
,但要从C++中的X::X()构造函数来执行。
我想在X::X()构造函数中使用类似
scope().attr("y")=...
的方式,但每次这样调用时,范围总是返回一个NoneType对象(如果我在BOOST_PYTHON_MODULE中使用这个构造是可以正常工作的,但那不是我想要的地方)。
1 个回答
2
boost::python::scope
更像是命名空间,而不是代码块的作用域。Python/C API 通过PyEval_GetLocals()
函数暴露了一个类似于locals()
的字典。我们可以利用这个字典将变量注入到当前的作用域中。
// Borrow a reference from the locals dictionary to create a handle.
// If PyEval_GetLocals() returns NULL, then Boost.Python will throw.
namespace python = boost::python;
python::object locals(python::borrowed(PyEval_GetLocals()));
// Inject an instance of Y into the frame's locals as variable 'y'.
// Boost.Python will handle the conversion of C++ Y to Python Y.
locals["y"] = Y();
下面是一个完整的例子,当构造example.X
时,会将example.Y
的实例作为变量y
注入到调用者的作用域中。
#include <boost/python.hpp>
/// @brief Mockup types.
struct X {};
struct Y {};
/// @brief Auxiliary function that will create X and inject an Y object
/// as 'y' into the caller's frame.
X* make_x_and_inject_y()
{
// Boost.Python objects may throw, so use a smart pointer that can
// release ownership to manage memory.
std::auto_ptr<X> x(new X());
// Borrow a reference from the locals dictionary to create a handle.
// If PyEval_GetLocals() returns NULL, then Boost.Python will throw.
namespace python = boost::python;
python::object locals(python::borrowed(PyEval_GetLocals()));
// Inject an instance of Y into the frame's locals as variable 'y'.
// Boost.Python will handle the conversion of C++ Y to Python Y.
locals["y"] = Y();
// Transfer ownership of X to Boost.Python.
return x.release();
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
// Expose X, explicitly suppressing Boost.Python from creating a
// default constructor, and instead exposing a custom constructor.
python::class_<X>("X", python::no_init)
.def("__init__", python::make_constructor(&make_x_and_inject_y))
;
python::class_<Y>("Y", python::init<>());
}
交互使用:
>>> import example
>>> def fun():
... assert('y' not in dir())
... example.X()
... assert('y' in dir()) # creating X injects y into scope
...
>>> assert('y' not in dir())
>>> fun()
>>> assert('y' not in dir())
>>> example.X()
<example.X object at 0xb746fa7c>
>>> assert('y' in dir()) # creating X injects y into scope
>>> assert(isinstance(y, example.Y))
在这个实现中,我选择将一个辅助工厂函数暴露给Python,作为X
的构造函数,而不是让X
的C++构造函数来执行Y
的注入。这只是个人偏好,但我发现这样可以更清晰地区分两种语言,减少C++类型对Python的了解。