Boost.python将对象拉入本地作用域以进行读取访问

时间:2014-11-26 00:08:33

标签: python c++ boost scope boost-python

我正在使用Boost.Python将C ++函数导出到Python。在这个函数中,我想访问本地堆栈框架上的Python对象,没有需要在Python端访问该对象。

示例:我有一个C ++对象X和一个C ++函数cppfun导出到Python,然后是代码,

x = X()

def fun():
    cppfun()
    # no explicit access to x

cppfun内部我可以使用PyEval_GetLocals访问本地堆栈帧,但是因为。{1}} Python端不包含对x的任何引用,此对象不在该局部堆栈帧中(类似地,如果print(locals())funx将不存在除非您添加对它的引用,例如print(x))。

我是否有办法在x内访问cppfun,也就是说,我可以以某种方式强制Python将其拉入locals而无需在fun内访问boost::python::eval("x")在Python方面?我试过简单地运行x,但也运行在错误的范围内。

已添加:所以我想让外框中的fun可写在{{1}}内或类似的东西(我知道)这是不可能的);这个问题纯粹是关于如何从外部框架获取对变量的读访问权,而无需在Python端访问它。

1 个答案:

答案 0 :(得分:0)

可以通过使用PyEval_GetGlobals()来访问外部范围,globals()返回当前执行框架中全局变量的字典。它与Python内置PyEval_GetFrame()函数非常相似,后者返回定义函数的模块的全局符号表。但是,标准模块和扩展模块之间的帧堆栈处理方式略有不同。对于扩展模块,当前执行 frame 是调用者,因此PyEval_GetGlobals()将返回调用者模块的全局符号,而不是扩展模块的全局符号

备选方案,可以通过{{3}}获取当前帧的句柄,然后遍历堆栈,检查每个的本地(f_local)和全球(f_global)字典。


这是一个完整的最小例子,展示了两种技术以及它们之间的细微差别。在示例中,get_global()将使用PyEval_GetGlobals()search_stack_locals()将检查堆栈中每个帧的本地字典。

#include <boost/python.hpp>

namespace detail {

/// @brief Return a handle to a global variable.  If one is not found, then
///        None is returned.
boost::python::object get_global(std::string name)
{
  // Borrow a reference from the locals dictionary to create a handle.
  // If PyEval_GetGlobals() returns NULL, then Boost.Python will throw.
  namespace python = boost::python;
  python::dict globals(python::borrowed(PyEval_GetGlobals()));
  return globals.get(name);
}

/// @brief Search through the call stack for a variable.  If found, the
///        object is returned.  Otherwise, None.
boost::python::object search_stack_locals(std::string name)
{
  // Get a handle to the current frame.
  namespace python = boost::python;
  python::object frame(python::borrowed(
    reinterpret_cast<PyObject*>(PyEval_GetFrame())));

  // Iterate through the stack's frames until the variable has been found
  // or the stack has been exhausted.
  while (frame)
  {
    // If the current frame has the desired variable, then return it.
    python::dict locals(frame.attr("f_locals"));
    if (locals.has_key(name))
    {
      return locals.get(name);
    }

    // Move up the stack.
    frame = frame.attr("f_back");
  }

  // Return None
  return python::object();
}

} // namespace detail

/// @brief Mockup function to demonstrate finding non-local variables.
boost::python::object cppfun(std::string name)
{
  return boost::python::make_tuple(
    detail::get_global(name), detail::search_stack_locals(name));
}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::def("cppfun", &cppfun);
}

spam.py

import example

x = 'spam.global.x'
y = 'spam.global.y'

def inner():
    for name in ('x', 'y', 'z'):
        print name, example.cppfun(name)

def outer():
    x = 'spam.outer.x'
    inner()

交互式使用:

>>> x = 'main.global.x'
>>> y = 'main.global.y'
>>> z = 'main.global.z'
>>> import spam
>>> spam.outer()
x ('spam.global.x', 'spam.outer.x')
y ('spam.global.y', 'main.global.y')
z (None, 'main.global.z')

请注意,使用PyEval_GetGlobals()时,example扩展程序模块使用了来电者模块的全局符号表(spam)。在解释器的主命名空间中声明的全局变量只能在迭代堆栈时找到。