Область действия функции eval в python
Рассмотрим следующий пример:
i=7
j=8
k=10
def test():
i=1
j=2
k=3
return dict((name,eval(name)) for name in ['i','j','k'])
Он возвращает:
>>> test()
{'i': 7, 'k': 10, 'j': 8}
Почему eval не принимает во внимание переменные, определенные внутри функции? Из документации вы можете передать глобальные и локальные словари. Что это значит? Наконец, как я могу изменить этот маленький случай, чтобы он работал?
Ответы
Ответ 1
Генераторы реализованы как области функций:
Объем имен, определенных в блоке класса, ограничен классом блок; он не распространяется на кодовые блоки методов - это включает выражения генератора, поскольку они реализованы с использованием область действия.
Итак, генератор внутри конструктора dict()
имеет свой собственный словарь locals()
. Теперь рассмотрим Py_eval
исходный код, особенно когда оба globals()
и locals()
равны None:
if (globals == Py_None) {
globals = PyEval_GetGlobals();
if (locals == Py_None)
locals = PyEval_GetLocals();
}
Итак, для вашего примера PyEval_GetLocals()
будет пустым в момент выполнения цикла, а globals()
будет глобальным словарем. Обратите внимание, что i
, j
и k
, определенные внутри функции, не находятся в локальной области генератора, а находятся в его охватывающей области:
>>> dict((name,eval(name, globals(), {})) for name in ['i', 'j', 'k'])
{'i': 7, 'k': 10, 'j': 8}
Ответ 2
Это происходит потому, что выражение генератора имеет различную область для функции:
>>> def test():
i, j, k = range(1, 4)
return dict((j, locals()) for _ in range(i))
>>> test()
{2: {'.0': <listiterator object at 0x02F50A10>, 'j': 2, '_': 0}}
Использование j
внутри области связывает ее с функцией, так как ближайшая охватывающая область, но i
и k
не связаны локально (поскольку k
не ссылается и i
только используется для создания range
).
Обратите внимание, что вы можете избежать этой проблемы с помощью:
return dict(i=i, j=j, k=k)
или словарный литерал:
return {'i': i, 'j': j, 'k': k}