Почему лямбда-функция в eval не может закрывать переменные в пользовательском диктанте 'locals'? - PullRequest
0 голосов
/ 29 мая 2018

Я хочу оценить лямбда-выражение, используя встроенную функцию eval, с переменной y, определенной в аргументе 'locals'.К сожалению, функция результата не работает:

>>>x = eval('lambda: print(y)',{},{'y':2})
>>>x()
Traceback (most recent call last):
  File "<pyshell#75>", line 1, in <module>
    x()
  File "<string>", line 1, in <lambda>
NameError: name 'y' is not defined

Но с y, определенным в аргументе 'globals', она работает:

>>> x = eval('lambda: print(y)', {'y': 2},{})
>>> x()
2

Как я понимаю, лямбдавыражение должно было охватить весь текущий фрейм, включая все переменные, определенные в аргументах globals и localals.

Так почему же Python ведет себя так?

Ответы [ 2 ]

0 голосов
/ 29 мая 2018

Проще говоря: передача заполненного каталога locals не меняет способ, которым python анализирует код функции и решает, какие имена являются локальными, а какие глобальными.

Локальные имена - это имена аргументов и имена, которые связаны внутри тела функции и явно не объявлены как глобальные или нелокальные.Здесь y не является аргументом и не связан в теле функции (что в любом случае невозможно в lambda), поэтому он помечается компилятором как глобальный.

Теперь эта глобальная и локальная среда - это те, которые используются для оценки самого выражения (здесь полное выражение «лямбда: печать (у)»), а не «локальная среда для тела лямбды», так что даже если естьбыл способ сделать y локальным по отношению к телу функции (подсказка: есть одна - но это не решит вашу проблему) это имя все равно не будет «автоматически» установлено на значение «y» в локальных данных, переданных dictдо eval.

НО на самом деле это не проблема - функция, созданная eval("lambda: y", {"y":42}), записывает глобальный запрос, переданный в eval, и использует его вместо модуля / скрипта / любых глобальных переменных, поэтому она будет работать как положено:

Python 3.4.3 (default, Nov 28 2017, 16:41:13) 
[GCC 4.8.4] on linux
>>> f = eval("lambda: y+2", {'y':2}, {})
>>> f()
4
>>> y = 42
>>> f()
4

Теперь у вас есть объяснение, я хочу добавить, что eval() очень опасно и чаще всего существует гораздо лучшее решение, в зависимости от конкретной проблемы, с которой вы столкнулись.Пытаюсь решить.Вы не предоставили никакого контекста, поэтому невозможно сказать больше, но я должен сказать, что на самом деле мне трудно думать о конкретном сценарии использования такой вещи, как f = eval("lambda: whatever").

0 голосов
/ 29 мая 2018

x = eval('lambda: print(y)',{},{'y':2}) не равно этой строке x = eval('lambda: print(y)', {'y': 2},{}) в первом порядке изменения параметров, и оно должно работать

...