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

428

Szukałem na dynamiczną ocenę kodu Pythona i natknąć się eval()i compile()funkcji, a execstwierdzenie.

Czy ktoś mógłby wyjaśnić różnicę między evala execi jak różne tryby compile()wpasować?

andrewdotnich
źródło

Odpowiedzi:

517

Krótka odpowiedź lub TL; DR

Zasadniczo evalsłuży do eval uate pojedynczy dynamicznie generowane wyrażenie Python, i execsłuży do exec ute dynamicznie generowanego kodu Pythona tylko jego skutki uboczne.

evali execmają te dwie różnice:

  1. evalakceptuje tylko jeden wyraz , execmogą wziąć blok kodu, który ma oświadczenia Pythonie pętli try: except:, classa funkcja / metoda definitions 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 execignoruje wartość zwracaną z kodu i zawsze zwraca None(w Pythonie 2 jest to instrukcja i nie może być używana jako wyrażenie, więc tak naprawdę nic nie zwraca).

W wersjach 1.0 - 2.7 execbyła instrukcją, ponieważ CPython musiał stworzyć inny rodzaj obiektu kodu dla funkcji, które wykorzystywały execefekty uboczne wewnątrz funkcji.

W Pythonie 3 execjest funkcją; jego użycie nie ma wpływu na skompilowany kod bajtowy funkcji, w której jest używany.


Zatem 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

compileW 'exec'trybie kompiluje dowolną liczbę sprawozdań do kodu bajtowego, która w domyśle zawsze zwraca None, natomiast w 'eval'trybie on kompiluje jeden wyraz do kodu bajtowego, który powraca 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 'eval'trybie (a więc z evalfunkcją, jeśli przekazywany jest ciąg znaków), compilepodnosi wyjątek, jeśli kod źródłowy zawiera instrukcje lub cokolwiek innego poza jednym 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 jedno wyrażenie” ma zastosowanie tylko wtedy, gdy przekazany jest ciąg znaków (zawierający kod źródłowy Pythona ) eval. Następnie jest wewnętrznie kompilowany do kodu bajtowego za pomocą compile(source, '<string>', 'eval')To właśnie stąd naprawdę pochodzi różnica.

Jeśli codeobiekt (który zawiera kod bajtowy Pythona ) jest przekazywany do execlub eval, zachowują się identycznie , z wyjątkiem faktu, że execignoruje zwracaną wartość, wciąż zwraca wartość None. Możliwe jest więc użycie evalczegoś, co zawiera instrukcje, jeśli po prostu compiled wprowadzisz go do kodu bajtowego przed przekazaniem go jako łańcucha:

>>> 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 z obiektu kodu zwróconego compile.

W 'eval'trybie (a więc z evalfunkcją, jeśli przekazywany jest ciąg znaków), compilepodnosi wyjątek, jeśli kod źródłowy zawiera instrukcje lub cokolwiek innego poza jednym 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

Im dłuższa odpowiedź, czyli krwawe szczegóły

exec i eval

execFunkcja (co było oświadczenie w Pythonie 2 ) służy do wykonywania dynamicznie utworzony oświadczenia lub programu:

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

evalFunkcja robi to samo dla pojedynczego wyrazu , i zwraca wartość wyrażenia:

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

execi evaloba akceptują uruchamianie programu / wyrażenia jako str, unicodelub bytesobiektu zawierającego kod źródłowy, lub jako codeobiekt zawierający kod bajtowy Pythona.

Jeśli przekazano kod źródłowy zawierający str/ unicode/ , zachowuje się on tak samo, jak:bytesexec

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

i evalpodobnie zachowuje się jak:

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

Ponieważ wszystkie wyrażenia mogą być używane w Pythonie jako instrukcje (są one nazywane Exprwęzłami w gramatyce abstrakcyjnej Pythona ; odwrotność nie jest prawdą), zawsze możesz ich użyć, execjeśli nie potrzebujesz wartości zwracanej. Innymi słowy, możesz użyć albo, eval('my_func(42)')albo z exec('my_func(42)')tą różnicą, że evalzwraca wartość zwróconą przez my_funci execodrzuca 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 execakceptuje kodu źródłowego, który zawiera instrukcje, jak def, for, while, import, lub class, oświadczenie przypisanie (aka a = 42) lub całe programy:

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

Zarówno execi evalprzyjąć 2 dodatkowe argumenty pozycyjne - globalsi locals- które są zmienne globalne i lokalne celownicze, że kod widzi. Domyślnie są w zakresie globals()i locals()w zakresie, który wywołał execlub eval, ale można użyć dowolnego słownika globalsi mappingdla dowolnego locals(w tym dictoczywiście). Można ich używać nie tylko do ograniczania / modyfikowania zmiennych, które widzi kod, ale często są również używane do przechwytywania zmiennych exectworzonych przez kod Uted:

>>> 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łości g, byłaby ona znacznie dłuższa, ponieważ execi automatycznie evaldodajesz wbudowany moduł __builtins__globali, jeśli go brakuje).

W Pythonie 2 oficjalna składnia execinstrukcji jest tak exec code in globals, localsjak w

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

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

compile

compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)Wbudowanej może być stosowany w celu przyspieszenia powtarzających wywołania tego samego kodu z execlub evalkompilując źródła do codeobiektu z wyprzedzeniem. Ten modeparametr kontroluje rodzaj fragmentu kodu compileakceptowanego przez funkcję oraz rodzaj tworzonego kodu bajtowego. Do wyboru są 'eval', 'exec'i 'single':

  • 'eval'Tryb oczekuje pojedynczego wyrażenia i wygeneruje kod bajtowy, który po uruchomieniu zwróci wartość tego wyrażenia :

    >>> 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 python od pojedynczych wyrażeń do całych modułów kodu i wykonuje je tak, jakby były modułowymi instrukcjami najwyższego poziomu. Obiekt kodu 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 pojedynczą instrukcję (lub wiele instrukcji oddzielonych ;), jeśli ostatnia instrukcja jest instrukcją wyrażenia, wynikowy kod bajtowy wypisujerepr również wartość tego wyrażenia na standardowe wyjście (!) .

    if- elif- elsełańcuch, pętla z elseoraz tryz jego except, elsea finallybloki są uważane za pojedynczy rachunek.

    Fragment źródłowy zawierający 2 instrukcje najwyższego poziomu jest błędem 'single', z wyjątkiem tego , że w Pythonie 2 występuje błąd, który czasami dopuszcza wiele instrukcji najwyższego poziomu w kodzie; tylko pierwsza jest kompilowana; reszta jest ignorowana:

    W Python 2.7.8:

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

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

    Jest to bardzo przydatne do tworzenia interaktywnych powłok Pythona. Jednak wartość wyrażenia nie jest zwracana , nawet jeśli evalwynikowy kod.

Zatem największe rozróżnienie execi evalfaktycznie pochodzi od compilefunkcji i jej trybów.


Oprócz kompilowania kodu źródłowego do kodu bajtowego, compileobsługuje kompilowanie abstrakcyjnych drzew składni (parsowanie drzew kodu Python) na codeobiekty; i kod źródłowy w abstrakcyjne drzewa składniowe ( ast.parsejest napisany w Pythonie i po prostu 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ługiwać kod jako drzewo węzłów zamiast wierszy tekstu w skomplikowanych przypadkach.


Chociaż evalpozwala tylko na ocenę ciągu zawierającego pojedyncze wyrażenie, możesz evalcałą instrukcję, a nawet cały moduł, który został compileprzekształcony w kod bajtowy; to znaczy, że w Pythonie 2 printjest instrukcją i nie można jej evalprowadzić bezpośrednio:

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

compilez 'exec'trybem na codeobiekt i możesz to eval zrobić ; evalfunkcja 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 patrzy się evali execkodu źródłowego w CPython 3, jest to bardzo widoczne; oba wywołują PyEval_EvalCodez tymi samymi argumentami, jedyną różnicą jest to, że execjawnie zwracaNone .

Różnice w składni execmiędzy Python 2 i Python 3

Jedną z głównych różnic w Pythonie 2 jest to, że execjest instrukcją i evaljest funkcją wbudowaną (obie są funkcjami wbudowanymi w Pythonie 3). Powszechnie wiadomo, że oficjalna składnia execw języku Python 2 to exec code [in globals[, locals]].

W przeciwieństwie do większości przewodników portowania 2 do 3 w Pythonie wydaje się , że instrukcja w CPython 2 może być również używana ze składnią, która wygląda dokładnie tak , jak wywołanie funkcji w Pythonie 3. Powodem jest to, że Python 0.9.9 miał wbudowane w działaniu! I ta wbudowana funkcja została zastąpiona instrukcją gdzieś przed wydaniem Python 1.0 . exec execexec(code, globals, locals)exec

Ponieważ pożądane było, aby nie zerwać wstecznej zgodności z Pythonem 0.9.9, Guido van Rossum dodał hack kompatybilności w 1993 roku : jeśli byłby codekrotkę o długości 2 lub 3 globalsi localsnie zostałby przekazany do execinstrukcji, codeinterpretacja byłaby interpretowana tak jakby drugim i trzecim elementem krotki były odpowiednio globalsi locals. Hack kompatybilności nie został wspomniany nawet w dokumentacji Python 1.4 (najwcześniejsza dostępna wersja online) ; dlatego wielu pisarzy przewodników i narzędzi do przenoszenia nie było znane, dopóki nie zostało to ponownie udokumentowane w listopadzie 2012 r . :

Pierwszym wyrażeniem może być krotka o długości 2 lub 3. W takim przypadku części opcjonalne należy pominąć. Formularz exec(expr, globals)jest równoważny exec expr in globals, podczas gdy formularz exec(expr, globals, locals)jest równoważny exec expr in globals, locals. Krotkowa forma execzapewnia zgodność z Pythonem 3, gdzie execjest funkcją, a nie instrukcją.

Tak, w CPython 2.7, który jest łatwo określany jako opcja kompatybilności wstecznej (po co mylić ludzi z tym, że w ogóle istnieje opcja kompatybilności wstecznej), skoro tak naprawdę istniała tam od dwóch dekad .

Zatem chociaż execistnieje instrukcja w Python 1 i Python 2, a wbudowana funkcja w Python 3 i Python 0.9.9,

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

zachowywał się identycznie w każdej możliwej powszechnie wydanej wersji Pythona; i działa również w Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) i IronPython 2.6.1 (pochwały dla nich po nieudokumentowanym zachowaniu CPython).

To, czego nie można zrobić w Pythons 1.0 - 2.7 z jego hackem zgodności, to przechowywanie zwracanej wartości execw 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 nie byłoby przydatne w Pythonie 3, jak execzawsze zwraca None), lub przekazanie odwołania do exec:

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

Który wzór mógł być rzeczywiście użyty, choć mało prawdopodobny;

Lub użyj go do zrozumienia listy:

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

co jest nadużyciem rozumienia listy ( forzamiast tego użyj pętli!).

Antti Haapala
źródło
Czy [i for i in globals().values() if hasattr(i, '__call__')][0]oświadczenie lub wyrażenie? Jeśli to było wyrażenie, dlaczego nie mogę używać go @jako dekoratora?
Mario,
to jest wyrażenie. 42jest również wyrażeniem i nie można go używać @jako dekoratora.
Antti Haapala
Składnia dekoratora to decorator ::= "@" dotted_name ["(" [parameter_list [","]] ")"] NEWLINE; tzn. nie można używać dowolnych wyrażeń jako dekoratorów, TYLKO (być może kropkowanego) identyfikatora, a następnie opcjonalnych argumentów wywołania.
Antti Haapala
1
Nic, co można umieścić po prawej stronie zadania i nadal kompilować, jest wyrażeniem. Na przykład a = b = cjest to całkowicie poprawne stwierdzenie, podobnie jak jego prawa strona b = c- co nie jest wyrażeniem.
Tom
194
  1. execnie jest wyrażeniem: instrukcja w Pythonie 2.xi funkcja w Pythonie 3.x. Kompiluje i natychmiast ocenia instrukcję lub zestaw instrukcji zawartych w ciągu. 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. evaljest funkcją wbudowaną ( nie jest instrukcją), która ocenia wyrażenie i zwraca wartość wytwarzaną przez to 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. compilejest niższą wersją execi eval. Nie wykonuje ani nie ocenia twoich instrukcji ani wyrażeń, ale zwraca obiekt kodu, który może to zrobić. Tryby są następujące:

    1. compile(string, '', 'eval')zwraca obiekt kodu, który zostałby wykonany, gdybyś to zrobił eval(string). Zauważ, że nie możesz używać instrukcji w tym trybie; poprawne jest tylko (pojedyncze) wyrażenie.
    2. compile(string, '', 'exec')zwraca obiekt kodu, który zostałby wykonany, gdybyś to zrobił exec(string). Możesz użyć dowolnej liczby instrukcji tutaj.
    3. compile(string, '', 'single')działa jak exectryb, ale zignoruje wszystko oprócz pierwszej instrukcji. Zauważ, że instrukcja if/ elsez jej wynikami jest uważana za pojedynczą instrukcję.
Max Shawabkeh
źródło
40
W Pythonie 3 exec()jest teraz funkcją.
Tim Pietzcker,
2
Ponieważ (jak zauważyłeś) execjest stwierdzeniem w wersji, na którą celujesz, zwodnicze jest włączenie tych parenów, a jeśli spróbujesz użyć in globals, locals, również błędny.
Mike Graham
2
@MikeGraham exec obsługuje nawiasy i działa jak wywołanie w Pythonie 2 .
Antti Haapala,
2
@AnttiHaapala, o ile zadanie „obsługuje nawiasy”, ponieważ możesz to zrobić x = (y), to może być prawda. Inną funkcją przekształconą w instrukcję jest print; porównaj wynik print(1, 2, 3)w
pytonie
1
@habnabit nie tak. Proszę przeczytać tutaj moją odpowiedź i być zaskoczonym.
Antti Haapala,
50

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

wyrażenie oznacza „coś”, a wyrażenie oznacza „zrób coś”.

Wu Li
źródło
9
Drugi akapit jest takim uproszczeniem, że prawie staje się kłamstwem, wyrażenie może bardzo wiele zrobić, jeśli zawiera wywołanie funkcji.
Antti Haapala