Qual è la differenza tra eval, exec e compile?


428

Ho cercato a valutazione dinamica di codice Python, e venire attraverso l' eval()e compile()funzioni, e la execdichiarazione.

Qualcuno può spiegare la differenza tra evale exece come si compile()adattano le diverse modalità ?

Risposte:


517

La risposta breve, o TL; DR

Fondamentalmente, evalviene utilizzato per eval UATE una singola espressione Python generato dinamicamente, e execviene utilizzato per exec ute generato dinamicamente codice Python solo per i suoi effetti collaterali.

evale exechanno queste due differenze:

  1. evalaccetta solo una singola espressione , execpuò accettare un blocco di codice con istruzioni Python: loop try: except:, classe funzioni / metodi defe così via.

    Un'espressione in Python è qualunque cosa tu possa avere come valore in un'assegnazione variabile:

    a_variable = (anything you can put within these parentheses is an expression)
  2. eval restituisce il valore dell'espressione data, mentre execignora il valore restituito dal suo codice e restituisce sempre None(in Python 2 è un'istruzione e non può essere utilizzata come espressione, quindi in realtà non restituisce nulla).

Nelle versioni 1.0 - 2.7, execera un'affermazione, perché CPython doveva produrre un diverso tipo di oggetto codice per le funzioni usate execper i suoi effetti collaterali all'interno della funzione.

In Python 3, execè una funzione; il suo utilizzo non ha alcun effetto sul bytecode compilato della funzione in cui viene utilizzato.


Quindi sostanzialmente:

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

Il compilein 'exec'modalità compila un qualsiasi numero di dichiarazioni in un bytecode che implicitamente restituisce sempre None, mentre in 'eval'modalità si compila una singola espressione in bytecode che restituisce il valore di tale espressione.

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

Nella 'eval'modalità (e quindi con la evalfunzione se viene passata una stringa), compilegenera un'eccezione se il codice sorgente contiene istruzioni o qualsiasi altra cosa oltre una singola espressione:

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

In realtà l'istruzione "eval accetta solo una singola espressione" si applica solo quando viene passata una stringa (che contiene il codice sorgente Python ) eval. Quindi viene compilato internamente in bytecode usando compile(source, '<string>', 'eval')Questo è da dove proviene davvero la differenza.

Se un codeoggetto (che contiene il bytecode Python ) viene passato execo eval, si comportano in modo identico , ad eccezione del fatto che execignora il valore restituito, sempre restituendo Nonesempre. Quindi è possibile usare evalper eseguire qualcosa che ha delle istruzioni, se lo hai appena compileinserito in bytecode invece di passarlo come una stringa:

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

funziona senza problemi, anche se il codice compilato contiene istruzioni. Restituisce ancora None, perché quello è il valore restituito dall'oggetto codice restituito compile.

Nella 'eval'modalità (e quindi con la evalfunzione se viene passata una stringa), compilegenera un'eccezione se il codice sorgente contiene istruzioni o qualsiasi altra cosa oltre una singola espressione:

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

La risposta più lunga, ovvero i dettagli cruenti

exec e eval

La execfunzione (che era un'istruzione in Python 2 ) viene utilizzata per eseguire un'istruzione o un programma creati dinamicamente:

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

La evalfunzione fa lo stesso per una singola espressione , e restituisce il valore dell'espressione:

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

exece evalsia accettare il programma / espressione da eseguire sia come str, unicodeo bytescodice sorgente oggetto contenente, o come un codeoggetto che contiene bytecode Python.

Se un str/ unicode/ bytescontenente il codice sorgente è stato passato a exec, si comporta in modo equivalente a:

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

e si evalcomporta allo stesso modo equivalente a:

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

Dato che tutte le espressioni possono essere usate come istruzioni in Python (questi sono chiamati Exprnodi nella grammatica astratta di Python ; non è vero il contrario), puoi sempre usare execse non hai bisogno del valore restituito. Vale a dire, è possibile utilizzare uno eval('my_func(42)')o exec('my_func(42)'), la differenza è che evalrestituisce il valore restituito da my_funce lo execscarta:

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

Del 2, solo execaccetta il codice sorgente che contiene affermazioni, come def, for, while, import, o class, l'istruzione di assegnazione (alias a = 42), o interi programmi:

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

Entrambi exece evalaccettano 2 ulteriori argomenti posizionali - globalse locals- che sono gli ambiti delle variabili globali e locali che il codice vede. Questi sono predefiniti globals()e locals()nell'ambito che ha chiamato execo eval, ma è possibile utilizzare qualsiasi dizionario per globalse qualsiasi mappingper locals(incluso dictovviamente). Questi possono essere usati non solo per limitare / modificare le variabili che il codice vede, ma spesso sono anche usati per catturare le variabili execcreate dal codice uted:

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

(Se si visualizza il valore dell'intero g, sarebbe molto più lungo, perché exece evalaggiungere automaticamente il modulo integrato __builtins__ai globuli se manca).

In Python 2, la sintassi ufficiale per l' execistruzione è in realtà exec code in globals, locals, come in

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

Tuttavia, anche la sintassi alternativa exec(code, globals, locals)è sempre stata accettata (vedi sotto).

compile

Il compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)built-in può essere utilizzato per accelerare ripetute invocazioni dello stesso codice con execo evalcompilando in codeanticipo la fonte in un oggetto. Il modeparametro controlla il tipo di frammento di codice compileaccettato dalla funzione e il tipo di bytecode che produce. Le scelte sono 'eval', 'exec'e 'single':

  • 'eval'mode si aspetta una singola espressione e produrrà bytecode che quando eseguito restituirà il valore di quell'espressione :

    >>> 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'accetta qualsiasi tipo di costrutto Python da singole espressioni a interi moduli di codice e li esegue come se fossero istruzioni di livello superiore del modulo. L'oggetto codice restituisce 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'è una forma limitata 'exec'che accetta un codice sorgente contenente una singola istruzione (o più istruzioni separate da ;) se l'ultima istruzione è un'istruzione di espressione, il bytecode risultante stamparepr anche il valore di quella espressione sull'output standard (!) .

    Un if- elif- elsecatena, un ciclo con else, e trycon la sua except, elsee finallyblocchi è considerato una singola istruzione.

    Un frammento di sorgente contenente 2 istruzioni di livello superiore è un errore per 'single', tranne che in Python 2 è presente un bug che a volte consente più istruzioni di livello superiore nel codice; viene compilato solo il primo; il resto viene ignorato:

    In Python 2.7.8:

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

    E 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

    Questo è molto utile per creare shell Python interattive. Tuttavia, il valore dell'espressione non viene restituito , anche se si ottiene evalil codice risultante.

Quindi la più grande distinzione exece in evalrealtà viene dalla compilefunzione e dalle sue modalità.


Oltre a compilare il codice sorgente in bytecode, compilesupporta la compilazione di alberi di sintassi astratti (alberi di analisi del codice Python) in codeoggetti; e il codice sorgente in alberi di sintassi astratti ( ast.parseè scritto in Python e chiama semplicemente compile(source, filename, mode, PyCF_ONLY_AST)); questi vengono utilizzati ad esempio per modificare il codice sorgente al volo e anche per la creazione di codice dinamico, poiché è spesso più semplice gestire il codice come un albero di nodi anziché linee di testo in casi complessi.


Mentre evalti permette solo di valutare una stringa che contiene una singola espressione, puoi evalun'intera istruzione, o anche un intero modulo che è stato compileconvertito in bytecode; vale a dire, con Python 2, printè un'istruzione e non può essere evalguidato direttamente:

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

compilecon 'exec'modalità in un codeoggetto e puoi eval farlo ; la evalfunzione tornerà 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

Se uno esamina evale execil codice sorgente in CPython 3, questo è molto evidente; entrambi chiamano PyEval_EvalCodecon gli stessi argomenti, l'unica differenza è che execritorna esplicitamenteNone .

Differenze di sintassi exectra Python 2 e Python 3

Una delle principali differenze in Python 2 è che execè un'istruzione ed evalè una funzione integrata (entrambe sono funzioni integrate in Python 3). È risaputo che la sintassi ufficiale di execin Python 2 è exec code [in globals[, locals]].

Diversamente dalla maggior parte delle guide di porting da 2 a 3 di Python sembrano suggerire , l' istruzione in CPython 2 può anche essere usata con una sintassi che assomiglia esattamente alla chiamata della funzione in Python 3. Il motivo è che Python 0.9.9 aveva il build- in funzione! E quella funzione integrata è stata sostituita con un'istruzione da qualche parte prima della versione 1.0 di Python . exec execexec(code, globals, locals)exec

Dal momento che era desiderabile non rompere la compatibilità con Python 0.9.9, nel 1993 Guido van Rossum aggiunse un hack di compatibilità : se codefosse una tupla di lunghezza 2 o 3 globalse localsnon fosse passata execnell'affermazione in caso contrario, codesarebbe interpretata come se il 2o e il 3o elemento della tupla fossero rispettivamente globalse locals. L'hack di compatibilità non è stato menzionato nemmeno nella documentazione di Python 1.4 (la prima versione disponibile online) ; e quindi non era noto a molti scrittori delle guide e degli strumenti per il porting, fino a quando non è stato nuovamente documentato nel novembre 2012 :

La prima espressione può anche essere una tupla di lunghezza 2 o 3. In questo caso, le parti opzionali devono essere omesse. Il modulo exec(expr, globals)è equivalente a exec expr in globals, mentre il modulo exec(expr, globals, locals)è equivalente a exec expr in globals, locals. La forma tupla di execfornisce la compatibilità con Python 3, dove execè una funzione piuttosto che un'istruzione.

Sì, in CPython 2.7 viene definita come un'opzione di compatibilità diretta (perché confondere le persone sul fatto che esiste un'opzione di compatibilità con le versioni precedenti), quando in realtà era lì da due decenni per la compatibilità con le versioni precedenti .

Quindi, mentre execè un'istruzione in Python 1 e Python 2 e una funzione integrata in Python 3 e Python 0.9.9,

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

ha avuto un comportamento identico in ogni versione di Python ampiamente diffusa di sempre; e funziona anche in Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) e IronPython 2.6.1 (complimenti per loro seguendo da vicino il comportamento non documentato di CPython).

Quello che non puoi fare in Pythons 1.0 - 2.7 con il suo hack di compatibilità, è memorizzare il valore di ritorno execin una variabile:

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

(che non sarebbe utile neanche in Python 3, come execsempre restituisce None), o passa un riferimento a exec:

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

Quale modello che qualcuno avrebbe potuto effettivamente usare, sebbene improbabile;

O usalo nella comprensione di un elenco:

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

che è un abuso della comprensione dell'elenco (utilizzare forinvece un ciclo!).


Era [i for i in globals().values() if hasattr(i, '__call__')][0]un'affermazione o espressione? Se fosse un'espressione, perché non posso usarlo @come decoratore?
Mario,

è un'espressione. 42è anche un'espressione e non puoi usarla @come decoratore.
Antti Haapala,

La sintassi del decoratore è decorator ::= "@" dotted_name ["(" [parameter_list [","]] ")"] NEWLINE; cioè non puoi usare espressioni arbitrarie come decoratori, SOLO un identificatore (forse punteggiato), seguito da argomenti di chiamata opzionali.
Antti Haapala,

1
Non tutto ciò che può essere messo sul lato destro di un compito e comunque essere compilato è un'espressione. Ad esempio, a = b = cè un'affermazione perfettamente valida, così come il suo lato destro b = c- che non è un'espressione.
Tom,

194
  1. execnon è un'espressione: un'istruzione in Python 2.xe una funzione in Python 3.x. Compila e valuta immediatamente un'istruzione o un insieme di istruzioni contenuti in una stringa. Esempio:

    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è una funzione incorporata ( non un'istruzione), che valuta un'espressione e restituisce il valore che produce l'espressione. Esempio:

    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è una versione di livello inferiore di exece eval. Non esegue o valuta le tue dichiarazioni o espressioni, ma restituisce un oggetto codice che può farlo. Le modalità sono le seguenti:

    1. compile(string, '', 'eval')restituisce l'oggetto codice che sarebbe stato eseguito se lo avessi fatto eval(string). Si noti che non è possibile utilizzare le istruzioni in questa modalità; è valida solo un'espressione (singola).
    2. compile(string, '', 'exec')restituisce l'oggetto codice che sarebbe stato eseguito se lo avessi fatto exec(string). Puoi usare qualsiasi numero di dichiarazioni qui.
    3. compile(string, '', 'single')è come la execmodalità, ma ignorerà tutto tranne la prima istruzione. Notare che un'istruzione if/ elsecon i suoi risultati è considerata una singola istruzione.

40
In Python 3, exec()ora è in effetti una funzione.
Tim Pietzcker,

2
Poiché (come fai notare), execè un'affermazione nella versione che stavi prendendo di mira, è ingannevole includere quelle parentesi e, se provi a usare in globals, locals, anche buggy.
Mike Graham,

2
@MikeGraham exec supporta le parentesi e funziona come l'invocazione in Python 2 .
Antti Haapala,

2
@AnttiHaapala nella misura in cui il compito 'supporta le parentesi' perché puoi farlo x = (y), potrebbe essere vero. Un'altra istruzione trasformata in funzione è print; confronta il risultato di print(1, 2, 3)in Python 2 e 3.
habnabit

1
@habnabit non è così. Si prega di leggere la parte inferiore della mia risposta qui ed essere sorpreso.
Antti Haapala,

50

exec è per dichiarazione e non restituisce nulla. eval è per espressione e restituisce valore di espressione.

espressione significa "qualcosa" mentre istruzione significa "fai qualcosa".


9
Il secondo paragrafo è una tale semplificazione che diventa quasi una menzogna, un'espressione può davvero fare qualcosa se include una chiamata di funzione.
Antti Haapala,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.