В чем разница между eval, exec и compile?

428

Я смотрел на динамической оценке кода Python, и попадался eval()и compile()функции, а также execутверждение.

Может кто-нибудь объяснить, пожалуйста, разницу между evalи exec, и как различные способы compile()вписываются?

andrewdotnich
источник

Ответы:

518

Краткий ответ, или TL; DR

В основном, evalиспользуются для Eval хать один динамически выражение Python, и execиспользуются для ехеса Юта динамически генерируемого код Python только для его побочных эффектов.

evalи execесть эти два различия:

  1. evalпринимает только одно выражение , execможет принимать блок кода, который имеет операторы Python: циклы try: except:, classи defинициации функций / методов и так далее.

    Выражение в Python - это то, что вы можете иметь в качестве значения в присваивании переменной:

    a_variable = (anything you can put within these parentheses is an expression)
  2. eval возвращает значение данного выражения, тогда как execигнорирует возвращаемое значение из своего кода и всегда возвращает None(в Python 2 это оператор и его нельзя использовать как выражение, поэтому он действительно ничего не возвращает).

В версиях 1.0 - 2.7 execбыло утверждение, потому что CPython должен был производить другой тип объекта кода для функций, которые использовали execдля побочных эффектов внутри функции.

В Python 3 execэто функция; его использование не влияет на скомпилированный байт-код функции, в которой он используется.


Таким образом, в основном:

>>> a = 5
>>> eval('37 + a')   # it is an expression
42
>>> exec('37 + a')   # it is an expression statement; value is ignored (None is returned)
>>> exec('a = 47')   # modify a global variable as a side effect
>>> a
47
>>> eval('a = 47')  # you cannot evaluate a statement
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    a = 47
      ^
SyntaxError: invalid syntax

В режиме compilein 'exec'компилируется любое количество операторов в байт-код, который неявно всегда возвращается None, тогда как в 'eval'режиме он компилирует одно выражение в байт-код, который возвращает значение этого выражения.

>>> eval(compile('42', '<string>', 'exec'))  # code returns None
>>> eval(compile('42', '<string>', 'eval'))  # code returns 42
42
>>> exec(compile('42', '<string>', 'eval'))  # code returns 42,
>>>                                          # but ignored by exec

В 'eval'режиме (и, следовательно, в evalфункции, если передается строка), compileвозникает исключение, если исходный код содержит операторы или что-то еще, кроме одного выражения:

>>> compile('for i in range(3): print(i)', '<string>', 'eval')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print(i)
      ^
SyntaxError: invalid syntax

На самом деле выражение «eval принимает только одно выражение» применяется только тогда, когда строка (которая содержит исходный код Python ) передана eval. Затем он внутренне компилируется в байт-код с использованием. compile(source, '<string>', 'eval')Именно в этом и заключается разница.

Если codeобъект (который содержит байт-код Python ) передается execили eval, они ведут себя одинаково , за исключением того факта, что execигнорируемое возвращаемое значение все еще возвращается Noneвсегда. Таким образом, можно использовать evalдля выполнения чего-то, что имеет операторы, если вы просто compileпередали это в байт-код, вместо того, чтобы передавать это как строку:

>>> eval(compile('if 1: print("Hello")', '<string>', 'exec'))
Hello
>>>

работает без проблем, хотя скомпилированный код содержит операторы. Это все еще возвращает None, потому что это возвращаемое значение объекта кода, возвращенного из compile.

В 'eval'режиме (и, следовательно, в evalфункции, если передается строка), compileвозникает исключение, если исходный код содержит операторы или что-то еще, кроме одного выражения:

>>> compile('for i in range(3): print(i)', '<string>'. 'eval')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print(i)
      ^
SyntaxError: invalid syntax

Чем дольше ответ, ака кровавые детали

exec а также eval

execФункция (которая была констатация в Python 2 ) используется для выполнения динамически созданного заявления или программы:

>>> program = '''
for i in range(3):
    print("Python is cool")
'''
>>> exec(program)
Python is cool
Python is cool
Python is cool
>>> 

evalФункция делает то же самое для одного выражения , и возвращает значение выражения:

>>> a = 2
>>> my_calculation = '42 * a'
>>> result = eval(my_calculation)
>>> result
84

execи evalоба принимают программу / выражение для запуска либо как str, unicodeлибо как bytesобъект, содержащий исходный код, либо как codeобъект, который содержит байт-код Python.

Если str/ unicode/ bytesсодержащий исходный код был передан exec, он ведет себя эквивалентно:

exec(compile(source, '<string>', 'exec'))

и evalаналогично ведет себя эквивалентно:

eval(compile(source, '<string>', 'eval'))

Поскольку все выражения могут использоваться как операторы в Python (они называются Exprузлами в абстрактной грамматике Python ; обратное неверно), вы всегда можете использовать, execесли вам не нужно возвращаемое значение. То есть вы можете использовать либо, eval('my_func(42)')либо exec('my_func(42)'), с той разницей, что evalвозвращает возвращаемое значение my_funcи execотбрасывает его:

>>> def my_func(arg):
...     print("Called with %d" % arg)
...     return arg * 2
... 
>>> exec('my_func(42)')
Called with 42
>>> eval('my_func(42)')
Called with 42
84
>>> 

Из 2, только execпринимает исходный код , который содержит заявление, как def, for, while, import, или class, оператор присваивания (он же a = 42), или целые программы:

>>> exec('for i in range(3): print(i)')
0
1
2
>>> eval('for i in range(3): print(i)')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print(i)
      ^
SyntaxError: invalid syntax

Оба execи evalпринимают 2 дополнительных позиционных аргумента - globalsи locals- которые являются областями глобальной и локальной переменных, которые видит код. По умолчанию это globals()и в locals()пределах объема, который вызывал execили eval, но любой словарь может быть использован для globalsи любой mappingдля locals(в том числе, dictконечно). Они могут использоваться не только для ограничения / изменения переменных, которые видит код, но также часто используются для захвата переменных, которые execсоздает указанный код:

>>> g = dict()
>>> l = dict()
>>> exec('global a; a, b = 123, 42', g, l)
>>> g['a']
123
>>> l
{'b': 42}

(Если отображается значение всего g, было бы гораздо больше, потому что execи evalдобавить встроенные модули модуль , как __builtins__для глобалов автоматически , если она отсутствует).

В Python 2 официальный синтаксис для execоператора на самом деле exec code in globals, locals, как в

>>> exec 'global a; a, b = 123, 42' in g, l

Однако альтернативный синтаксис exec(code, globals, locals)всегда был принят (см. Ниже).

compile

compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)Встроенный может быть использован для ускорения повторных вызовов одного и того же кода с execили evalпутем компиляции исходного в codeобъект заранее. В modeпараметре управляет вид фрагмента кода по compileфункции принимает и вид байткода она производит. Выбор есть 'eval', 'exec'и 'single':

  • 'eval'mode ожидает одно выражение и выдаст байт-код, который при запуске вернет значение этого выражения :

    >>> dis.dis(compile('a + b', '<string>', 'eval'))
      1           0 LOAD_NAME                0 (a)
                  3 LOAD_NAME                1 (b)
                  6 BINARY_ADD
                  7 RETURN_VALUE
  • 'exec'принимает любые типы конструкций Python от отдельных выражений до целых модулей кода и выполняет их, как если бы они были операторами верхнего уровня модуля. Код объекта возвращает None:

    >>> dis.dis(compile('a + b', '<string>', 'exec'))
      1           0 LOAD_NAME                0 (a)
                  3 LOAD_NAME                1 (b)
                  6 BINARY_ADD
                  7 POP_TOP                             <- discard result
                  8 LOAD_CONST               0 (None)   <- load None on stack
                 11 RETURN_VALUE                        <- return top of stack
  • 'single'является ограниченной формой, 'exec'которая принимает исходный код, содержащий один оператор (или несколько операторов, разделенных ;), если последний оператор является оператором выражения, результирующий байт-код также выводит reprзначение этого выражения в стандартный вывод (!) .

    if- elif- elseцепь, цикл с else, и tryс его except, elseи finallyблоки считается один оператор.

    Фрагмент исходного кода, содержащий 2 оператора верхнего уровня, является ошибкой для 'single', за исключением того, что в Python 2 есть ошибка, которая иногда допускает несколько операторов верхнего уровня в коде; компилируется только первое; остальные игнорируются:

    В Python 2.7.8:

    >>> exec(compile('a = 5\na = 6', '<string>', 'single'))
    >>> a
    5

    И в Python 3.4.2:

    >>> exec(compile('a = 5\na = 6', '<string>', 'single'))
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<string>", line 1
        a = 5
            ^
    SyntaxError: multiple statements found while compiling a single statement

    Это очень полезно для создания интерактивных оболочек Python. Однако значение выражения не возвращается , даже если вы evalполучили код.

Таким образом, наибольшее различие execи evalфактически происходит от compileфункции и ее режимов.


В дополнение к компиляции исходного кода в байт-код, compileподдерживает компиляцию абстрактных синтаксических деревьев (разбора деревьев кода Python) в codeобъекты; и исходный код в абстрактные синтаксические деревья ( ast.parseнаписан на Python и просто вызывает compile(source, filename, mode, PyCF_ONLY_AST)); они используются, например, для изменения исходного кода на лету, а также для динамического создания кода, поскольку в сложных случаях часто проще обрабатывать код как дерево узлов, а не строк текста.


Хотя evalвы можете вычислять только строку, содержащую одно выражение, вы можете evalиспользовать целый оператор или даже целый модуль, который был compileпреобразован в байт-код; то есть, с Python 2, printявляется оператором и не может быть evalприведен напрямую:

>>> eval('for i in range(3): print("Python is cool")')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
    for i in range(3): print("Python is cool")
      ^
SyntaxError: invalid syntax

compileэто с 'exec'модой в codeобъект, и вы можете eval это ; evalфункция будет возвращать None.

>>> code = compile('for i in range(3): print("Python is cool")',
                   'foo.py', 'exec')
>>> eval(code)
Python is cool
Python is cool
Python is cool

Если посмотреть на исходный код CPython 3 evalи его execисходный код, это очень очевидно; они оба вызывают PyEval_EvalCodeс одинаковыми аргументами, единственное отличие состоит в том, что execявно возвращаетNone .

Синтаксические различия execмежду Python 2 и Python 3

Одним из основных отличий в Python 2 является то, что он execявляется оператором и evalявляется встроенной функцией (обе являются встроенными функциями в Python 3). Это общеизвестный факт, что официальный синтаксис execв Python 2 exec code [in globals[, locals]].

В отличии от большинства Python 2-к-3 портирования руководства , кажется предложить , то execзаявление в CPython 2 может быть также использовано с синтаксисом , который выглядит точно , как execвызов функции в Python 3. Причина заключается в том, что Python 0.9.9 имел exec(code, globals, locals)встроенный в функции! И эта встроенная функция была заменена execоператором где-то перед выпуском Python 1.0 .

Так как это было бы желательно , чтобы не нарушить обратную совместимость с Python 0.9.9, Гвидо ван Россум добавил хак совместимости в 1993 году : если codeбыл кортеж длины 2 или 3, а также globalsи localsне были переданы в execзаявлении в противном случае, codeбудет интерпретироваться как будто 2-й и 3-й элемент кортежа были и globalsи localsсоответственно. Хакерская совместимость не упоминалась даже в документации по Python 1.4 (самая ранняя доступная версия онлайн) ; и, таким образом, не было известно многим авторам руководств по портированию и инструментов, пока он не был снова задокументирован в ноябре 2012 года :

Первое выражение также может быть кортежем длины 2 или 3. В этом случае необязательные части должны быть опущены. Форма exec(expr, globals)эквивалентна exec expr in globals, в то время как форма exec(expr, globals, locals)эквивалентна exec expr in globals, locals. Форма кортежа execобеспечивает совместимость с Python 3, где execявляется функцией, а не оператором.

Да, в CPython 2.7 его удобно называть опцией прямой совместимости (зачем путать людей с тем, что вообще существует опция обратной совместимости), когда он фактически существовал для обратной совместимости в течение двух десятилетий .

Таким образом, while execявляется оператором в Python 1 и Python 2 и встроенной функцией в Python 3 и Python 0.9.9,

>>> exec("print(a)", globals(), {'a': 42})
42

имел идентичное поведение, возможно, во всех широко выпущенных версиях Python; и работает в Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) и IronPython 2.6.1 (спасибо им за недокументированное поведение CPython).

То, что вы не можете сделать в Pythons 1.0 - 2.7 с его хаком совместимости, - это сохранить возвращаемое значение execв переменной:

Python 2.7.11+ (default, Apr 17 2016, 14:00:29) 
[GCC 5.3.1 20160413] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = exec('print(42)')
  File "<stdin>", line 1
    a = exec('print(42)')
           ^
SyntaxError: invalid syntax

(который не будет полезен в Python 3, как execвсегда возвращает None), или передайте ссылку на exec:

>>> call_later(exec, 'print(42)', delay=1000)
  File "<stdin>", line 1
    call_later(exec, 'print(42)', delay=1000)
                  ^
SyntaxError: invalid syntax

Какой паттерн может использовать кто-то, хотя и маловероятный;

Или используйте это в понимании списка:

>>> [exec(i) for i in ['print(42)', 'print(foo)']
  File "<stdin>", line 1
    [exec(i) for i in ['print(42)', 'print(foo)']
        ^
SyntaxError: invalid syntax

что является злоупотреблением пониманием списка (используйте forвместо этого цикл!).

Антти Хаапала
источник
Было [i for i in globals().values() if hasattr(i, '__call__')][0]ли заявление или выражение? Если это было выражение, почему я не могу использовать его в @качестве декоратора?
Марио
это выражение. 42также является выражением, и вы не можете использовать его @в качестве декоратора.
Антти Хаапала
Синтаксис декоратора decorator ::= "@" dotted_name ["(" [parameter_list [","]] ")"] NEWLINE: то есть вы не можете использовать произвольные выражения в качестве декораторов, ТОЛЬКО (возможно, пунктирный) идентификатор, за которым следуют необязательные аргументы вызова.
Антти Хаапала
1
Ничто из того, что можно поместить в правую часть присваивания и по-прежнему компилировать, не является выражением. Например, a = b = cэто совершенно допустимое утверждение, как и его правая часть, b = cкоторое не является выражением.
Том
194
  1. execне является выражением: оператор в Python 2.x и функция в Python 3.x. Он компилирует и сразу оценивает оператор или набор операторов, содержащихся в строке. Пример:

    exec('print(5)')           # prints 5.
    # exec 'print 5'     if you use Python 2.x, nor the exec neither the print is a function there
    exec('print(5)\nprint(6)')  # prints 5{newline}6.
    exec('if True: print(6)')  # prints 6.
    exec('5')                 # does nothing and returns nothing.
  2. evalявляется встроенной функцией ( не оператором), которая оценивает выражение и возвращает значение, которое производит выражение. Пример:

    x = eval('5')              # x <- 5
    x = eval('%d + 6' % x)     # x <- 11
    x = eval('abs(%d)' % -100) # x <- 100
    x = eval('x = 5')          # INVALID; assignment is not an expression.
    x = eval('if 1: x = 4')    # INVALID; if is a statement, not an expression.
  3. compileэто версия нижнего уровня execи eval. Он не выполняет и не оценивает ваши операторы или выражения, но возвращает объект кода, который может это сделать. Режимы следующие:

    1. compile(string, '', 'eval')возвращает объект кода, который был бы выполнен, если бы вы сделали eval(string). Обратите внимание, что вы не можете использовать операторы в этом режиме; допустимо только (одно) выражение.
    2. compile(string, '', 'exec')возвращает объект кода, который был бы выполнен, если бы вы сделали exec(string). Вы можете использовать любое количество утверждений здесь.
    3. compile(string, '', 'single')Это как execрежим, но он будет игнорировать все, кроме первого утверждения. Обратите внимание, что оператор if/ elseс его результатами считается одним оператором.
Макс Шавабке
источник
40
В Python 3 exec()теперь это фактически функция.
Тим Пицкер
2
Поскольку (как вы указали), execэто утверждение в версии, на которую вы нацеливались, обманчиво включать эти символы, и, если вы попытаетесь использовать in globals, locals, также ошибка.
Майк Грэм
2
@MikeGraham exec поддерживает круглые скобки и работает как вызов в Python 2 .
Антти Хаапала
2
@AnttiHaapala, поскольку задание «поддерживает круглые скобки», потому что вы можете это сделать x = (y), что может быть правдой. Другой оператор, превращенный в функцию, является print; сравнить результат print(1, 2, 3)в Python 2 и 3.
habnabit
1
@habnabit не так. Пожалуйста, прочтите нижнюю часть моего ответа здесь и удивитесь.
Антти Хаапала
50

exec для утверждения и ничего не возвращает. eval для выражения и возвращает значение выражения.

выражение означает «что-то», а выражение означает «сделать что-то».

Ву Ли
источник
9
Второй абзац настолько упрощен, что почти превращается в ложь, выражение может очень многое сделать, если оно включает вызов функции.
Антти Хаапала