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

Область Eval в Python 2 против 3

Я наткнулся на странное поведение eval в Python 3 - локальные переменные не подхвачены, когда eval вызывается в понимании списка.

def apply_op():
    x, y, z = [0.5, 0.25, 0.75]
    op = "x,y,z"
    return [eval(o) for o in op.split(",")]
print(apply_op())

Это ошибки в Python 3:

▶ python --version
Python 3.4.3
▶ python eval.py
Traceback (most recent call last):
  File "eval.py", line 7, in <module>
    print(apply_op())
  File "eval.py", line 5, in apply_op
    return [eval(o) % 1 for o in op.split(",")]
  File "eval.py", line 5, in <listcomp>
    return [eval(o) % 1 for o in op.split(",")]
  File "<string>", line 1, in <module>
NameError: name 'x' is not defined

И он отлично работает в Python 2:

▶ python --version
Python 2.7.8
▶ python eval.py
[0.5, 0.25, 0.75]

Перемещение за пределы понимания списка устраняет проблему.

def apply_op():
    x, y, z = [0.5, 0.25, 0.75]
    return [eval("x"), eval("y"), eval("z")]

Является ли это предполагаемым поведением, или это ошибка?

4b9b3361

Ответ 1

Для отслеживания ошибок есть закрытая проблема: Проблема 5242.

Разрешение для этой ошибки не будет исправлено.

Некоторые комментарии к проблеме:

Ожидается, и это будет легко исправить. Причина в том, что список понимание в 3.x использует пространство имен функций "под капотом" (в 2.x, они были реализованы как простой цикл). Потому что внутренние функции нужно знать, какие имена нужно получить от того, что включает пространство имен, имена ссылка на eval() не может быть вызвана функциями включения. Они должны либо быть локальными или глобальными.

eval(), вероятно, уже взломан, нет необходимости добавлять еще один хак чтобы он работал. Лучше просто избавиться от eval() и найти лучшее способ сделать то, что вы хотите сделать.

Ответ 2

Если вы хотите:

def apply_op():
    x, y, z = [0.5, 0.25, 0.75]
    op = "x,y,z"
    return [eval(o) for o in op.split(",")]
print(apply_op())

вам нужно будет захватить локальные и глобальные переменные, поскольку проблема заключается в том, что eval(o) совпадает с eval(o, globals(), locals()), но по мере того, как в функции генератора появляется eval, результаты этих функций не являются то же, что и в случае, когда eval не имеет функции обертывания, поэтому захватите их вне генератора и используйте их внутри:

def apply_op():
    x, y, z = [0.5, 0.25, 0.75]
    op = "x,y,z"
    _locals = locals()
    _globals = globals()
    return [eval(o, _globals, _locals) for o in op.split(",")]
print(apply_op())

Или лучше, поскольку x,y,z являются локальными, а строки - это только имена переменных:

def apply_op():
    x, y, z = [0.5, 0.25, 0.75]
    op = "x,y,z"
    _locals = locals()
    return [_locals[o] for o in op.split(",")]
print(apply_op())