Какая разница между eval, exec и компиляцией в Python?

Я изучаю динамическую оценку кода Python и нахожусь в функциях eval() и compile() и в выражении exec.

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

Ответ 1

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

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

eval и exec имеют следующие два отличия:

  • eval принимает только одиночное выражение, exec может принимать блок кода с инструкциями Python: циклы, try: except:, class и function/method def переходы и т.д.

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

    a_variable = (anything you can put within these parentheses is an expression)
    
  • 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

Режим compile в '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 d его в байт-код перед тем, чтобы передать его как строку:

>>> 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

Более длинный ответ, a.k.a детали gory

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.

Если a 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, оператор присваивания (aka 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' режим ожидает одно выражение и будет генерировать байт-код, который, когда run вернет значение этого выражения:

    >>> 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', которая принимает исходный код, содержащий оператор single (или несколько операторов, разделенных символом ;), если последний оператор является выражением, полученный байт-код также выводит repr значения этого выражения на стандартный вывод (!).

    An 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 d в байт-код; то есть с 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 it; функция 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

Если вы посмотрите на eval и exec исходный код в CPython 3, это очень очевидно; они оба называют 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-to-3 porting руководства показаться чтобы предложить, оператор 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 его можно назвать опцией прямой совместимости (зачем путать людей с тем, что есть опция обратной совместимости вообще), когда он фактически был там для обратной совместимости в течение двух десятилетий.

Таким образом, хотя 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!).

Ответ 2

  • 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.
    
  • 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.
    
  • compile - это версия более низкого уровня exec и eval. Он не выполняет или не оценивает ваши утверждения или выражения, а возвращает объект кода, который может это сделать. Режимы следующие:

    • compile(string, '', 'eval') возвращает объект кода, который был бы выполнен, если бы вы сделали eval(string). Обратите внимание, что вы не можете использовать операторы в этом режиме; действует только одно (одно) выражение.
    • compile(string, '', 'exec') возвращает объект кода, который был бы выполнен, если бы вы сделали exec(string). Здесь вы можете использовать любое количество операторов.
    • compile(string, '', 'single') похож на режим exec, но будет игнорировать все, кроме первого оператора. Обратите внимание, что оператор if/else с его результатами рассматривается как один оператор.

Ответ 3

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

выражение означает "что-то", в то время как утверждение означает "сделать что-то".