Подтвердить что ты не робот

Область действия функции 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 не принимает во внимание переменные, определенные внутри функции? Из документации вы можете передать глобальные и локальные словари. Что это значит? Наконец, как я могу изменить этот маленький случай, чтобы он работал?

4b9b3361

Ответ 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}