PyEval_GetLocals возвращает глобальные переменные?

Я пытаюсь получить доступ к локальным версиям Python из конструктора класса C++, экспортированного с boost.python, но 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)

который также печатает "42", несмотря на то, что "fun()" создал новую область видимости. Я ожидал бы, что имя 'xyz' снова выйдет из области видимости после выхода из функции fun(), и, таким образом, к тому времени, когда я достигну оператора print, у меня останется неопределенный xyz.

Что я делаю неправильно? Есть ли способ получить доступ к локальным именам из объекта или функции C++?

1 ответ

Решение

Я думаю, что тестовый пример может привести к ложному срабатыванию. Возможно ли, что вы забыли del xyz переменная до вызова fun()?

Определение функции создает локальную переменную для текущей области, которая ссылается на объект функции. Например:

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 чья 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 флаг, я должен был ссылаться locals() в fun() наблюдать x вставляется в глобальную таблицу символов.

Другие вопросы по тегам