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
вставляется в глобальную таблицу символов.