【问题标题】:PyEval_GetLocals returns globals?PyEval_GetLocals 返回全局变量?
【发布时间】:2014-04-12 16:19:00
【问题描述】:

我正在尝试从使用 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)

尽管 'fun()' 已经创建了一个新的范围,它也打印了 '42'。我本以为在 fun() 退出后“xyz”名称会再次超出范围,因此在我到达 print 语句时会留下未定义的“xyz”。

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

【问题讨论】:

  • 请注意,即使PyEval_GetLocals 会给你一个适当的本地人字典,它也会给你X::X() 的本地人*。词法作用域等等。要操纵 fun() 的范围,您需要弄乱帧。
  • 很公平,但是 PyEval_GetLocals 应该返回什么?还有一个 PyEval_GetGlobals,我认为这是一个非常强烈的暗示,这两个实际上返回了不同的东西。他们没有?

标签: python c++ boost-python globals locals


【解决方案1】:

我认为测试用例可能会导致误报。在调用 fun() 之前,您是否可能忘记了 del 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())

为了完整起见,CodeType 可以使用 co_flags 未设置 newlocals 标志的 CodeType 构造,从而导致用于函数调用的框架使其 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 被插入到全局符号表中。

【讨论】:

  • 是的,我的样本确实有效......我很愚蠢。很抱歉浪费您的时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-07
  • 2020-09-11
  • 2016-05-13
  • 2014-01-16
  • 1970-01-01
  • 1970-01-01
  • 2017-02-24
相关资源
最近更新 更多