当前位置: 代码迷 >> python >> PyEval_GetLocals返回全局变量?
  详细解决方案

PyEval_GetLocals返回全局变量?

热度:76   发布时间:2023-06-19 09:12:47.0

我正在尝试从使用boost.python导出的C ++类的构造函数中访问python locals,但是PyEval_GetLocals()似乎返回的是全局而不是本地dict。 一个例子:在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)

尽管'fun()'创建了一个新的作用域,但它也会打印'42'。 我本来希望'xyz'名称在fun()退出后会再次超出范围,因此在到达print语句时会留下未定义的'xyz'。

我究竟做错了什么? 有什么方法可以从C ++对象或函数中访问本地名称?

我认为测试用例可能会导致误报。 您是否有可能在调用fun()之前忘记了del xyz变量?

定义函数会在当前作用域本地创建一个引用该函数对象的变量。 例如:

def fun():
    x = X()

创建一个function对象,该对象由当前范围内的fun变量引用。 如果调用该函数,则(默认情况下)将创建一个新的本地范围,其中x将从X()返回的对象在该函数的本地范围内而不是在调用者框架的locals()内由x引用。


这是一个基于原始代码的示例:

#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())

为了完整可以使用构造一个 ,该的没有设置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() fun()来观察x插入到全局符号表中的情况。

  相关解决方案