Яка різниця між eval, exec та компіляцією?


428

Я дивився на динамічну оцінку коду Python, і натрапив на eval()і compile()функції, і execзаяву.

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

Відповіді:


517

Коротка відповідь, або TL; DR

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

    >>> 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об’єкти; і вихідний код у абстрактні синтаксичні дерева (the 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

Якщо вивчити 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цикл замість!).


Була [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
Другий абзац - це таке спрощення, що воно майже стає брехнею, вираз дуже може щось зробити, якщо він включає виклик функції.
Антті Хаапала
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.