В чем разница между eval, exec и compile? - PullRequest
379 голосов
/ 08 февраля 2010

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

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

Ответы [ 3 ]

431 голосов
/ 05 апреля 2015

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

В основном eval используется для eval использования одного динамически генерируемого выражения Python, а exec используется для 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

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

>>> 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 кровавые подробности

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

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

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

190 голосов
/ 08 февраля 2010
  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 с его результатами считается одним оператором.
49 голосов
/ 31 декабря 2012

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

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

...