Jaka jest różnica między eval, exec i kompilacji?

Przyglądałem się dynamicznej ocenie kodu Pythona i natknąłem się na funkcje eval() i compile() oraz instrukcję exec.

Czy ktoś może wyjaśnić różnicę między eval i exec i jak różne tryby compile() pasują do siebie?

Author: martineau, 2010-02-08

3 answers

Krótka odpowiedź, czyli TL;DR

Zasadniczo, eval jest używany do evaluate pojedynczego dynamicznie generowanego wyrażenia Pythona, oraz exec jest używany do execute dynamicznie generowany kod Pythona tylko dla jego efektów ubocznych.

eval i exec mają te dwie różnice:

  1. eval akceptuje tylko pojedyncze wyrażenie, exec może przyjmować blok kodu zawierający polecenia Pythona: loops, try: except:, class i inicjały function / method def i tak dalej.

    Wyrażenie w Pythonie jest tym, co możesz mieć jako wartość w przypisaniu zmiennej:

    a_variable = (anything you can put within these parentheses is an expression)
    
  2. eval Zwraca wartość podanego wyrażenia, podczas gdy exec ignoruje zwracaną wartość ze swojego kodu i zawsze zwraca None (w Pythonie 2 jest to polecenie i nie może być użyte jako wyrażenie, więc tak naprawdę niczego nie zwraca).

W wersji 1.0 - 2.7, exec było stwierdzeniem, ponieważ CPython musiał stworzyć inny rodzaj obiektu kodu dla funkcji, które używały exec do jego efektów ubocznych wewnątrz funkcji.

W Pythonie 3, {[25] } jest funkcją; jej użycie nie ma wpływu na skompilowany kod bajtowy funkcji, w której jest używana.


W zasadzie:

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

Tryb compile w trybie 'exec' kompiluje dowolną liczbę poleceń do kodu bajtowego, który domyślnie zawsze zwraca None, podczas gdy w 'eval' tryb kompiluje pojedynczewyrażenie w Bajt kodzie, który zwraca wartość tego wyrażenia.

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

W trybie 'eval' (a więc z funkcją eval Jeśli przekazywany jest łańcuch znaków), compile podnosi wyjątek, jeśli kod źródłowy zawiera instrukcje lub cokolwiek innego poza pojedynczym wyrażeniem:

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

W rzeczywistości Instrukcja "eval akceptuje tylko pojedyncze wyrażenie" stosuje się tylko wtedy, gdy łańcuch (który zawiera Python kod źródłowy ) jest przekazywany do eval. Następnie jest wewnętrznie kompilowany do kodu bajtowego za pomocą compile(source, '<string>', 'eval') to stąd naprawdę bierze się różnica.

Jeśli obiekt code (który zawiera Python bajtowy kod ) zostanie przekazany do exec lub eval, zachowują się identycznie , z wyjątkiem faktu, że exec ignoruje zwracaną wartość, wciąż zwracając None zawsze. Więc możliwe jest użycie eval do wykonania czegoś, co ma polecenia, jeśli tylko compiled do bajtowego kodu przed zamiast przekazywania go jako ciąg znaków:

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

Działa bez problemów, mimo że skompilowany kod zawiera instrukcje. Nadal zwraca None, ponieważ jest to wartość zwracana obiektu code zwróconego z compile.

W trybie 'eval' (a więc z funkcją eval Jeśli przekazywany jest łańcuch znaków), compile podnosi wyjątek, jeśli kod źródłowy zawiera instrukcje lub cokolwiek innego poza pojedynczym wyrażenie:

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

Dłuższa odpowiedź, a.k. a krwawe szczegóły

exec i eval

The exec funkcja (która była Instrukcją w Pythonie 2 ) jest używana do wykonywania dynamicznie tworzonych instrukcji lub programu:

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

The eval funkcja robi to samo dla pojedynczego wyrażenia, i Zwraca wartość wyrażenia:

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

exec i eval obie akceptują program/wyrażenie do uruchomienia jako str, unicode lub bytes obiekt zawierający kod źródłowy, lub jako code obiekt zawierający bajt kodu Pythona.

Jeśli str/unicode/bytes zawiera kod źródłowy przekazany do exec, zachowuje się równoważnie do:

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

I eval podobnie zachowuje się odpowiednik:

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

Ponieważ wszystkie wyrażenia mogą być używane jako wyrażenia w Pythonie (nazywane są one węzłami Expr w Pythonie streszczenie gramatyka; odwrotność nie jest prawdziwa), zawsze możesz użyć exec, jeśli nie potrzebujesz zwracanej wartości. Oznacza to, że możesz użyć eval('my_func(42)') lub exec('my_func(42)'), z tą różnicą, że eval Zwraca wartość zwróconą przez my_func, a exec odrzuca ją:

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

Z 2, tylko exec akceptuje kod źródłowy, który zawiera instrukcje, takie jakdef, for, while, import, w 1998 roku, po raz pierwszy w historii, w Polsce, w Polsce, w Polsce, w Polsce, w Polsce, w Polsce, w Polsce, w Polsce, w Polsce i za granicą.]}

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

Oba exec i eval akceptują 2 dodatkowe argumenty pozycyjne - globals i locals - które są globalnymi i lokalnymi zakresami zmiennych, które widzi kod. Te domyślne to globals() i locals() w zakresie, który nazywa się exec lub eval, ale każdy słownik może być użyty dla globals i dowolnego mapping dla locals (w tym oczywiście dict). Mogą one być używane nie tylko do ograniczania / modyfikowania zmiennych, które widzi kod, ale często są również używane do przechwytywania zmiennych, które kod uted {25]} tworzy:

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

(jeśli wyświetlisz wartość całego g, będzie to znacznie dłuższe, ponieważ exec i eval dodają wbudowany Moduł jako __builtins__ do globali automatycznie, jeśli go brakuje).

W Pythonie 2 Oficjalna składnia exec jest w rzeczywistości exec code in globals, locals, jak w
>>> exec 'global a; a, b = 123, 42' in g, l

Jednak składnia alternatywna exec(code, globals, locals) również zawsze była akceptowana (patrz niżej).

compile

The compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1) Wbudowany może być używany do przyspieszania powtarzających się wywołań tego samego kodu z exec lub eval przez kompilację źródła do obiektu code. Parametr mode kontroluje rodzaj fragmentu kodu akceptowanego przez funkcję compile oraz rodzaj wytwarzanego przez nią bajtowego kodu. Wybory są 'eval', 'exec' i 'single':

  • 'eval' mode oczekuje pojedynczego wyrażenia i wytworzy bajt, który po uruchomieniu zwróci wartość , która wyrażenie :

    >>> 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' akceptuje wszelkiego rodzaju konstrukcje Pythona, od pojedynczych wyrażeń po całe Moduły kodu, i wykonuje je tak, jakby były instrukcjami najwyższego poziomu modułu. Obiekt code zwraca 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' jest ograniczoną formą 'exec', która akceptuje kod źródłowy zawierający pojedyncze oświadczenie (lub wiele instrukcji oddzielonych ;) jeśli ostatnie oświadczenie jest wyrażeniem, również wypisuje repr wartość tego wyrażenia na standardowe wyjście (!).

    An if-elif-else łańcuch, pętla z else i {[130] } z jego except, else i finally bloki są uważane za pojedyncze stwierdzenie.

    Fragment źródłowy zawierający 2 wyrażenia najwyższego poziomu jest błędem 'single', z wyjątkiem Pythona 2 istnieje błąd , który czasami pozwala na wiele wyrażeń najwyższego poziomu w kodzie; tylko pierwszy jest skompilowany, reszta jest ignorowana:

    W Pythonie 2.7.8:

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

    I w Pythonie 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
    

    Jest to bardzo przydatne do tworzenia interaktywnych powłok Pythona. Jednak wartość wyrażenia to not return , nawet jeśli eval kod wynikowy.

Tak więc największe rozróżnienie exec i eval pochodzi od compile funkcji i jej trybów.


Oprócz kompilacji kodu źródłowego do bytecode, compile obsługuje kompilowanie abstrakcyjne drzewa składniowe ([141] } jest napisany w Pythonie i wywołuje compile(source, filename, mode, PyCF_ONLY_AST)); są one używane na przykład do modyfikowania kodu źródłowego w locie, a także do dynamicznego tworzenia kodu, ponieważ często łatwiej jest obsłużyć kod jako drzewo węzłów zamiast linii tekstu w złożonych przypadkach.


While eval pozwala tylko na aby ocenić łańcuch zawierający pojedyncze wyrażenie, możesz eval całą instrukcję lub nawet cały moduł, który został compiled w bajtowym kodzie; to znaczy, w Pythonie 2, print jest instrukcją i nie może być bezpośrednio wyprowadzony 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 go z trybem 'exec' W obiekt code i można eval to ; funkcja eval zwróci 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

Jeśli ktoś spojrzy na eval oraz exec kod źródłowy w CPython 3, to jest bardzo oczywiste; oba wywołują PyEval_EvalCode z tymi samymi argumentami, jedyną różnicą jest to, że exec jawnie zwraca None.

Różnice w składni exec pomiędzy Pythonem 2 a Pythonem 3

Jedna z głównych różnic w Pythonie 2 czy exec jest instrukcją, a {[24] } jest wbudowaną funkcją (obie są wbudowanymi funkcjami w Pythonie 3). Jest dobrze znany fakt, że oficjalna składnia exec w Pythonie 2 to exec code [in globals[, locals]].

Większość Pythona 2-to-3 portowanie Przewodniki seem aby zasugerować , Instrukcja exec W CPython 2 może być również używana ze składnią, która wygląda dokładnie jak wywołanie funkcji exec w Pythonie 3. Powodem jest to, że Python 0.9.9 miał wbudowaną funkcję exec(code, globals, locals)! I ta wbudowana funkcja została zastąpiona exec instrukcją gdzieś przed wydaniem Pythona 1.0.

Ponieważ pożądane było nie łamać wstecznej kompatybilności z Pythonem 0.9.9, Guido van Rossum dodał hack zgodności w 1993: jeśli code była krotką o długości 2 lub 3, a globals i locals nie zostały przekazane do exec w przeciwnym razie, code będzie interpretowane tak, jakby drugi i trzeci element krotki były odpowiednio globals i {92]}. Hakowanie zgodności nie zostało wspomniane nawet w dokumentacji Pythona 1.4 (Najwcześniejsza dostępna wersja online) ; i tak było nie jest znany wielu pisarzom przewodników i narzędzi do przenoszenia, dopóki nie został udokumentowany ([385]} {191]} ponownie ([387]} w listopadzie 2012:

Pierwsze wyrażenie może być krotką o długości 2 lub 3. W takim przypadku należy pominąć części opcjonalne. Forma exec(expr, globals) jest równoważna exec expr in globals, natomiast forma exec(expr, globals, locals) jest równoważna exec expr in globals, locals. Forma krotki exec zapewnia kompatybilność z Pythonem 3, gdzie exec jest funkcją, a nie oświadczenie.

Tak, w CPython 2.7, że jest to handly określane jako opcja forward-compatibility (po co mylić ludzi o to, że istnieje opcja kompatybilności wstecznej w ogóle), kiedy to faktycznie było tam dla wstecznej kompatybilności przez dwie dekady .

W Pythonie 1 i Pythonie 2, a wbudowana funkcja w Pythonie 3 i Pythonie 0.9.9, {198]}
>>> exec("print(a)", globals(), {'a': 42})
42

Miał identyczne zachowanie w możliwie każdym szeroko wydana wersja Pythona ever; działa również w Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) i IronPython 2.6.1 (chwała im za nieudokumentowane zachowanie CPython).

W Pythons 1.0 - 2.7 nie można zapisać wartości zwracanej exec do zmiennej:
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

(co również nie byłoby przydatne w Pythonie 3, ponieważ exec zawsze zwraca None)), lub przekazać odwołanie do exec:

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

Które a wzór, którego ktoś mógł użyć, choć mało prawdopodobny; {]}

Lub użyć go w spisie treści:

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

Czyli nadużywanie składanych list (zamiast tego użyj pętli for!).

 568
Author: Antti Haapala,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-10 16:47:39
  1. exec is not an expression: a statement in Python 2.x oraz funkcję w Pythonie 3.x. kompiluje i natychmiast ocenia polecenie lub zestaw instrukcji zawartych w łańcuchu znaków. Przykład:

     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 jest wbudowaną funkcją (, a nie instrukcją), która oblicza wyrażenie i zwraca wartość, którą wytworzy wyrażenie. Przykład:

     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 jest wersją niższego poziomu exec i eval. Nie wykonuje ani oceniaj swoje wyrażenia lub wyrażenia, ale zwraca obiekt kodu, który może to zrobić. Tryby są następujące:

  4. compile(string, '', 'eval') zwraca obiekt kodu, który zostałby wykonany, gdybyś to zrobił eval(string). Zauważ, że nie można używać instrukcji w tym trybie; tylko (pojedyncze) wyrażenie jest ważne.

  5. compile(string, '', 'exec') zwraca obiekt kodu, który zostałby wykonany, gdybyś to zrobił exec(string). Możesz użyć dowolnej liczby wyrażeń tutaj.

  6. compile(string, '', 'single') jest podobny do trybu exec, ale oczekuje dokładnie jednego wyrażenia/instrukcji, np compile('a=1 if 1 else 3', 'myf', mode='single')

 196
Author: Max Shawabkeh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-31 09:22:29

Exec jest dla instrukcji i nie zwraca niczego. eval służy do wyrażenia i zwraca wartość wyrażenia.

Wyrażenie oznacza "coś", podczas gdy wyrażenie oznacza "zrób coś".

 52
Author: Wu Li,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-31 04:02:10