PyEval_GetLocals 返回全局变量?
我正在尝试从一个用boost.python导出的C++类的构造函数中访问Python的局部变量,但发现PyEval_GetLocals()似乎返回的是全局变量,而不是局部变量字典。举个例子,在C++中我这样做:
class X {
public:
X() {
boost::python::object locals(boost::python::borrowed(PyEval_GetLocals()));
locals["xyz"]=42
}
};
BOOST_PYTHON_MODULE(test) {
class_<X>("X", init<>());
}
如果我现在在Python中这样做:
x = X()
print(xyz)
我得到的输出是'42'(这是预期的结果)。然而,使用下面的代码:
def fun():
x = X()
print(xyz)
同样也会打印'42',尽管'fun()'创建了一个新的作用域。我本来以为在'fun()'执行完后,'xyz'这个名字应该就不再可用了,所以当我到达打印语句时,'xyz'应该是未定义的。
我哪里做错了?有没有办法在C++对象或函数中访问局部变量名?
1 个回答
1
我觉得这个测试用例可能出现了误判。你有没有可能在调用 fun()
之前忘记删除 xyz
这个变量呢?
定义一个函数会在当前的作用域内创建一个变量,这个变量指向这个函数的对象。举个例子:
def fun():
x = X()
这段代码创建了一个 function
对象,这个对象在当前作用域内被 fun
变量引用。如果调用这个函数,默认情况下会创建一个新的局部作用域,在这个局部作用域内,从 X()
返回的对象会被 x
引用,而不是在调用者的环境中的 locals()
。
下面是一个基于原始代码的例子:
#include <boost/python.hpp>
/// @brief Mockup types.
struct X
{
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 a reference to the int(42) object as 'xyz' into the
// frame's local variables.
locals["xyz"] = 42;
}
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<X>("X", python::init<>());
}
交互式使用,确保可见性:
>>> import example
>>> def fun():
... assert('xyz' not in locals())
... x = example.X()
... assert('xyz' in locals())
... assert('xyz' not in globals())
...
>>> assert('xyz' not in globals())
>>> fun()
>>> assert('xyz' not in globals())
>>> x = example.X()
>>> assert('xyz' in globals())
>>> del xyz
>>> fun()
>>> assert('xyz' not in globals())
为了完整起见,可以构造一个 FuncionType
,它使用一个 CodeType
,这个 CodeType
的 co_flags
没有设置 newlocals
标志,这样在调用函数时使用的框架的 locals()
返回的结果和 globals()
是一样的。下面是一个演示这个的交互式使用例子:
>>> def fun():
... x = 42
... print "local id in fun:", id(locals())
...
>>> import types
>>> def no_locals(fn):
... func_code = fn.func_code
... return types.FunctionType(
... types.CodeType(
... func_code.co_argcount,
... func_code.co_nlocals,
... func_code.co_stacksize,
... func_code.co_flags & ~2, # disable newlocals
... func_code.co_code,
... func_code.co_consts,
... func_code.co_names,
... func_code.co_varnames,
... func_code.co_filename,
... func_code.co_name,
... func_code.co_firstlineno,
... func_code.co_lnotab),
... globals())
...
>>> id(globals())
3075430164L
>>> assert('x' not in locals())
>>> fun()
local id in fun: 3074819588
>>> assert('x' not in locals())
>>> fun = no_locals(fun) # disable newlocals flag for fun
>>> assert('x' not in locals())
>>> fun()
local id in fun: 3075430164
>>> assert('x' in locals())
>>> x
42
即使在禁用 newlocals
标志后,我仍然需要在 fun()
内部调用 locals()
,才能看到 x
被插入到全局符号表中。