Was ist der Unterschied zwischen eval, exec und compile?

428

Ich habe bei dynamischer Auswertung von Python - Code suchen, und komme über die eval()und compile()Funktionen, und die execAussage.

Kann jemand bitte den Unterschied zwischen evalund erklären execund wie die verschiedenen Modi compile()passen?

andrewdotnich
quelle

Antworten:

517

Die kurze Antwort oder TL; DR

Grundsätzlich evalwird verwendet , um eval Uate ein einziger dynamisch erzeugte Python Ausdruck und execwird verwendet exec ute dynamisch Python Code nur erzeugt , für die Nebenwirkungen.

evalund exechaben diese zwei Unterschiede:

  1. evalnur akzeptiert einzigen Ausdruck , execkann einen Codeblock nehmen , die Python - Anweisungen aufweist: Schleifen try: except:, classund die Funktion / Methode definitions und so weiter.

    Ein Ausdruck in Python ist alles, was Sie als Wert in einer Variablenzuweisung haben können:

    a_variable = (anything you can put within these parentheses is an expression)
  2. eval Gibt den Wert des angegebenen Ausdrucks zurück, während execder Rückgabewert aus seinem Code ignoriert wird und immer zurückgegeben wird None(in Python 2 ist es eine Anweisung und kann nicht als Ausdruck verwendet werden, sodass wirklich nichts zurückgegeben wird).

In den Versionen 1.0 - 2.7 execwar dies eine Anweisung, da CPython eine andere Art von Codeobjekt für Funktionen erstellen musste, die execfür ihre Nebenwirkungen innerhalb der Funktion verwendet wurden.

In Python 3 execist eine Funktion; Ihre Verwendung hat keine Auswirkung auf den kompilierten Bytecode der Funktion, in der sie verwendet wird.


Also im Grunde:

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

Der compileIn- 'exec'Modus kompiliert eine beliebige Anzahl von Anweisungen in einen Bytecode, der implizit immer zurückgibt None, während er im 'eval'Modus einen einzelnen Ausdruck in einen Bytecode kompiliert , der den Wert dieses Ausdrucks zurückgibt .

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

Im 'eval'Modus (und damit mit der evalFunktion, wenn eine Zeichenfolge übergeben wird) compilelöst das eine Ausnahme aus, wenn der Quellcode Anweisungen oder etwas anderes enthält, das über einen einzelnen Ausdruck hinausgeht:

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

Tatsächlich gilt die Anweisung "eval akzeptiert nur einen einzigen Ausdruck" nur, wenn eine Zeichenfolge (die Python- Quellcode enthält ) an übergeben wird eval. Dann wird es intern mit Bytecode kompiliert. compile(source, '<string>', 'eval')Hier kommt der Unterschied wirklich her.

Wenn ein codeObjekt (das Python- Bytecode enthält ) an execoder übergeben wird eval, verhalten sie sich identisch , mit Ausnahme der Tatsache, dass execder Rückgabewert ignoriert wird und immer zurückgegeben wird None. Es ist also möglich eval, etwas auszuführen, das Anweisungen enthält, wenn Sie compilees vorher nur in Bytecode geschrieben haben, anstatt es als Zeichenfolge zu übergeben:

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

funktioniert ohne Probleme, obwohl der kompilierte Code Anweisungen enthält. Es wird weiterhin zurückgegeben None, da dies der Rückgabewert des Codeobjekts ist, von dem zurückgegeben wird compile.

Im 'eval'Modus (und damit mit der evalFunktion, wenn eine Zeichenfolge übergeben wird) compilelöst das eine Ausnahme aus, wenn der Quellcode Anweisungen oder etwas anderes enthält, das über einen einzelnen Ausdruck hinausgeht:

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

Die längere Antwort, auch bekannt als die blutigen Details

exec und eval

Die execFunktion (die in Python 2 eine Anweisung war ) wird zum Ausführen einer dynamisch erstellten Anweisung oder eines Programms verwendet:

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

Die evalFunktion macht das gleiche für einen einzelnen Ausdruck , und gibt den Wert des Ausdrucks:

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

execund evalakzeptiert sowohl das Programm / Ausdruck als eine Person entweder werden str, unicodeoder bytesObjekt enthält Quellcode oder als codeObjekt , das Python Bytecode enthält.

Wenn ein str// unicode/ bytesenthaltender Quellcode an übergeben wurde exec, verhält er sich wie folgt:

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

und evalverhält sich ähnlich wie:

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

Da alle Ausdrücke in Python als Anweisungen verwendet werden können (diese werden Exprin der abstrakten Python- Grammatik als Knoten bezeichnet ; das Gegenteil ist nicht der Fall), können Sie sie immer verwenden, execwenn Sie den Rückgabewert nicht benötigen. Das heißt, Sie können entweder eval('my_func(42)')oder verwenden exec('my_func(42)'), mit dem Unterschied, dass evalder von zurückgegebene Wert zurückgegeben my_funcund execverworfen wird:

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

Von den 2 nur execakzeptiert Quellcode, die Anweisungen enthält, wie def, for, while, import, oder class, die Zuweisungsanweisung (aka a = 42) oder ganze Programme:

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

Beide execund evalakzeptieren 2 zusätzliche Positionsargumente - globalsund locals-, die die globalen und lokalen Variablenbereiche sind, die der Code sieht. Diese sind standardmäßig auf globals()und locals()innerhalb des Bereichs, der aufgerufen hat, execoder eval, aber jedes Wörterbuch kann für globalsund jedes mappingfür locals(einschließlich dictnatürlich) verwendet werden. Diese können nicht nur verwendet werden, um die im Code execangezeigten Variablen einzuschränken / zu ändern, sondern werden häufig auch zum Erfassen der Variablen verwendet, die der uted-Code erstellt:

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

(Wenn Sie den Wert des gesamten anzeigen g, wäre es viel länger, weil execund evalfügen Sie das eingebaute Modul __builtins__automatisch zu den Globals hinzu, wenn es fehlt).

In Python 2 execlautet die offizielle Syntax für die Anweisung tatsächlich exec code in globals, localswie in

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

Die alternative Syntax exec(code, globals, locals)wurde jedoch immer auch akzeptiert (siehe unten).

compile

Die compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)integrierte Funktion kann verwendet werden, um wiederholte Aufrufe desselben Codes mit execoder evaldurch vorheriges Kompilieren der Quelle in ein codeObjekt zu beschleunigen . Der modeParameter steuert die Art des von der compileFunktion akzeptierten Codefragments und die Art des von ihr erzeugten Bytecodes. Die Auswahlmöglichkeiten sind 'eval', 'exec'und 'single':

  • 'eval'mode erwartet einen einzelnen Ausdruck und erzeugt einen Bytecode, der beim Ausführen den Wert dieses Ausdrucks zurückgibt :

    >>> 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'Akzeptiert alle Arten von Python-Konstrukten, von einzelnen Ausdrücken bis hin zu ganzen Codemodulen, und führt sie so aus, als wären sie Anweisungen der obersten Ebene des Moduls. Das Codeobjekt gibt Folgendes zurück 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'eine beschränkte Form ist 'exec'der einen , die einen Quellencode akzeptiert einzelne Anweisung (oder mehrere Anweisungen getrennt durch ;) , wenn die letzte Anweisung ist eine Ausdrucksanweisung, die resultierende Bytecode auch druckt den reprdes Wertes dieses Ausdrucks in der Standardausgabe (!) .

    Eine if- elif- elseKette, eine Schleife mit elseund trymit seinen except, elseund finallyBlöcke wird eine einzelne Anweisung berücksichtigt.

    Ein Quellfragment mit 2 Anweisungen der obersten Ebene ist ein Fehler für die 'single', außer in Python 2 gibt es einen Fehler , der manchmal mehrere Anweisungen der obersten Ebene im Code zulässt. nur der erste wird zusammengestellt; der Rest wird ignoriert:

    In Python 2.7.8:

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

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

    Dies ist sehr nützlich, um interaktive Python-Shells zu erstellen. Der Wert des Ausdrucks wird jedoch nicht zurückgegeben , selbst wenn Sie evalden resultierenden Code verwenden.

Die größte Unterscheidung von execund evalkommt tatsächlich von der compileFunktion und ihren Modi.


compileUnterstützt neben dem Kompilieren von Quellcode zu Bytecode auch das Kompilieren abstrakter Syntaxbäume (Analysebäume von Python-Code) in codeObjekte. und Quellcode in abstrakte Syntaxbäume (der ast.parseist in Python geschrieben und ruft nur auf compile(source, filename, mode, PyCF_ONLY_AST)); Diese werden beispielsweise zum schnellen Ändern des Quellcodes und auch zur dynamischen Codeerstellung verwendet, da es in komplexen Fällen häufig einfacher ist, den Code als Knotenbaum anstelle von Textzeilen zu behandeln.


Während Sie evalnur eine Zeichenfolge auswerten können, die einen einzelnen Ausdruck enthält, können Sie evaleine ganze Anweisung oder sogar ein ganzes Modul, das compilein Bytecode umgewandelt wurde, verwenden. Das heißt, mit Python 2 printist eine Anweisung und kann nicht direkt evalgeführt werden:

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

compilees mit 'exec'Modus in ein codeObjekt und Sie können eval es ; Die evalFunktion wird zurückgegeben 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

Wenn man sich in evalund execQuellcode in CPython 3, dann ist dies sehr deutlich; Beide rufen PyEval_EvalCodemit denselben Argumenten auf. Der einzige Unterschied besteht darin, dass sie execexplizit zurückgegeben werdenNone .

Syntaxunterschiede execzwischen Python 2 und Python 3

Einer der Hauptunterschiede in Python 2 besteht darin, dass execes sich um eine Anweisung und evaleine integrierte Funktion handelt (beide sind in Python 3 integrierte Funktionen). Es ist eine bekannte Tatsache, dass die offizielle Syntax von execin Python 2 ist exec code [in globals[, locals]].

Im Gegensatz Mehrheit der Python 2-zu-3 - Portierung Führer scheinen vorzuschlagen , die execAussage in CPython 2 kann auch mit Syntax verwendet wird, sieht genau wie der execFunktionsaufruf in Python 3. Der Grund dafür ist , dass Python 0.9.9 das hatte exec(code, globals, locals)integrierte in Funktion! Und diese eingebaute Funktion wurde irgendwo vor der Veröffentlichung von Python 1.0 durch eine execAnweisung ersetzt .

Da es wünschenswert war, nicht zu brechen die Abwärtskompatibilität mit Python 0.9.9, eine Kompatibilität Hack 1993 Guido van Rossum hinzugefügt : Wenn das codeein Tupel der Länge 2 oder 3 war, und globalsund localswurden nicht in die vergangen execsonst Aussage, die codewürde interpretiert werden als ob das zweite und dritte Element des Tupels waren globalsund localssind. Der Kompatibilitäts-Hack wurde selbst in der Python 1.4-Dokumentation (der frühesten online verfügbaren Version) nicht erwähnt . und war daher vielen Autoren der Portierungsanleitungen und -werkzeuge nicht bekannt, bis sie im November 2012 erneut dokumentiert wurden :

Der erste Ausdruck kann auch ein Tupel der Länge 2 oder 3 sein. In diesem Fall müssen die optionalen Teile weggelassen werden. Das Formular exec(expr, globals)entspricht exec expr in globals, während das Formular exec(expr, globals, locals)entspricht exec expr in globals, locals. Die Tupelform von execbietet Kompatibilität mit Python 3, wobei execes sich eher um eine Funktion als um eine Anweisung handelt.

Ja, in CPython 2.7 wird es leicht als Vorwärtskompatibilitätsoption bezeichnet (warum die Leute darüber verwirren, dass es überhaupt eine Abwärtskompatibilitätsoption gibt), als es tatsächlich zwei Jahrzehnte lang für die Abwärtskompatibilität da war .

Während while execeine Anweisung in Python 1 und Python 2 und eine integrierte Funktion in Python 3 und Python 0.9.9 ist,

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

hat sich in möglicherweise jeder weit verbreiteten Python-Version jemals identisch verhalten; und funktioniert auch in Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) und IronPython 2.6.1 (ein großes Lob an sie, die das undokumentierte Verhalten von CPython genau verfolgen).

Was Sie in Pythons 1.0 - 2.7 mit seinem Kompatibilitäts-Hack nicht tun können, ist, den Rückgabewert von execin einer Variablen zu speichern :

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

(was in Python 3 auch nicht nützlich wäre, da execimmer zurückgegeben wird None) oder einen Verweis an Folgendes übergeben exec:

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

Welches Muster, das jemand tatsächlich verwendet haben könnte, obwohl unwahrscheinlich;

Oder verwenden Sie es in einem Listenverständnis:

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

Das ist Missbrauch des Listenverständnisses (verwenden Sie forstattdessen eine Schleife!).

Antti Haapala
quelle
War [i for i in globals().values() if hasattr(i, '__call__')][0]eine Aussage oder ein Ausdruck? Wenn es ein Ausdruck war, warum kann ich ihn nicht @als Dekorateur verwenden?
Mario
es ist ein Ausdruck. 42ist auch ein Ausdruck, und Sie können ihn nicht @als Dekorateur verwenden.
Antti Haapala
Die Dekorationssyntax lautet decorator ::= "@" dotted_name ["(" [parameter_list [","]] ")"] NEWLINE: Das heißt, Sie können keine beliebigen Ausdrücke als Dekoratoren verwenden, NUR eine (möglicherweise gepunktete) Kennung, gefolgt von optionalen Aufrufargumenten.
Antti Haapala
1
Nichts, was auf die rechte Seite einer Aufgabe gestellt und dennoch kompiliert werden kann, ist ein Ausdruck. Zum Beispiel a = b = cist eine vollkommen gültige Aussage, ebenso wie ihre rechte Seite b = c- was kein Ausdruck ist.
Tom
194
  1. execist kein Ausdruck: eine Anweisung in Python 2.x und eine Funktion in Python 3.x. Es kompiliert und wertet sofort eine Anweisung oder einen Satz von Anweisungen aus, die in einer Zeichenfolge enthalten sind. Beispiel:

    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. evalist eine integrierte Funktion ( keine Anweisung), die einen Ausdruck auswertet und den Wert zurückgibt, den der Ausdruck erzeugt. Beispiel:

    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. compileist eine niedrigere Version von execund eval. Es führt Ihre Anweisungen oder Ausdrücke nicht aus oder wertet sie nicht aus, sondern gibt ein Codeobjekt zurück, das dies kann. Die Modi sind wie folgt:

    1. compile(string, '', 'eval')Gibt das Codeobjekt zurück, das ausgeführt worden wäre, wenn Sie dies getan hätten eval(string). Beachten Sie, dass Sie in diesem Modus keine Anweisungen verwenden können. Es ist nur ein (einzelner) Ausdruck gültig.
    2. compile(string, '', 'exec')Gibt das Codeobjekt zurück, das ausgeführt worden wäre, wenn Sie dies getan hätten exec(string). Sie können hier beliebig viele Anweisungen verwenden.
    3. compile(string, '', 'single')ist wie der execModus, aber es wird alles außer der ersten Anweisung ignoriert. Beachten Sie, dass eine if/ else-Anweisung mit ihren Ergebnissen als einzelne Anweisung betrachtet wird.
Max Shawabkeh
quelle
40
In Python 3 ist exec()jetzt tatsächlich eine Funktion.
Tim Pietzcker
2
Da (wie Sie betonen) execeine Aussage in der Version ist, auf die Sie abzielen, täuscht es, diese Parens einzuschließen, und wenn Sie versuchen, den zu verwenden in globals, locals, auch fehlerhaft.
Mike Graham
2
@MikeGraham exec unterstützt Klammern und funktioniert wie ein Aufruf in Python 2 .
Antti Haapala
2
@AnttiHaapala, sofern die Zuweisung 'Klammern unterstützt', weil Sie dies tun x = (y)können, könnte dies zutreffen. Eine andere Anweisung-Turned-Funktion ist print; Vergleiche das Ergebnis von print(1, 2, 3)in Python 2 und 3.
Habnabit
1
@habnabit nicht so. Bitte lesen Sie den unteren Teil meiner Antwort hier und seien Sie überrascht.
Antti Haapala
50

exec steht für Anweisung und gibt nichts zurück. eval steht für expression und gibt den Wert von expression zurück.

Ausdruck bedeutet "etwas", während Aussage "etwas tun" bedeutet.

Wu Li
quelle
9
Der zweite Absatz ist eine solche Vereinfachung, dass es fast zu einer Lüge wird. Ein Ausdruck kann sehr viel bewirken, wenn er einen Funktionsaufruf enthält.
Antti Haapala