我正在使用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())
内fun
,x
将不存在除非您添加对它的引用,例如print(x)
)。
我是否有办法在x
内访问cppfun
,也就是说,我可以以某种方式强制Python将其拉入locals
而无需在fun
内访问boost::python::eval("x")
在Python方面?我试过简单地运行x
,但也运行在错误的范围内。
已添加:所以我不想让外框中的fun
可写在{{1}}内或类似的东西(我知道)这是不可能的);这个问题纯粹是关于如何从外部框架获取对变量的读访问权,而无需在Python端访问它。
答案 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
)。在解释器的主命名空间中声明的全局变量只能在迭代堆栈时找到。