从C ++中创建本地范围内的Python对象

时间:2014-03-08 23:33:45

标签: python c++ scope boost-python

我正在使用boost.python为Python提供两个C ++类,

class X {
   public:
      X();
}
class Y {
   ...
}

BOOST_PYTHON_MODULE(test) {
    class_<X>("X", init<>());
    class_<Y>("Y", init<>());
}

每当我在Python中创建一个新的X时,我想在C ++中运行一些代码,它们创建一个Y类型的本地对象'y'。所以当我做的时候有效

x = X()
在Python中,我希望这个运行

y = Y()

,但是来自C ++中的X :: X()构造函数。

我想要使用像

这样的东西
scope().attr("y")=...

在X :: X()构造函数中,但是当以这种方式调用时,作用域总是返回一个NoneType对象(如果我在BOOST_PYTHON_MODULE中使用这个结构,它可以正常工作,但对我来说这不是正确的地方)。

1 个答案:

答案 0 :(得分:2)

boost::python::scope更类似于命名空间而不是代码块的范围。 Python / C API通过locals()函数公开类似于PyEval_GetLocals()的字典。可以使用此字典将变量注入当前范围。

// 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.Y的实例在构造y时作为变量example.X注入调用者的范围。

#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的注入。这只是个人偏好,但我经常发现它通过限制了解Python的C ++类型的数量,在语言之间提供了更清晰的界限。