Qual é a diferença entre eval, exec e compile?


428

Eu estive olhando para avaliação dinâmica de código Python, e se deparar com o eval()e compile()funções, e o execcomunicado.

Alguém pode explicar a diferença entre evale exec, e como os diferentes modos de compile()adaptação se encaixam?

Respostas:


517

A resposta curta, ou TL; DR

Basicamente, evalé usado para Eval uate uma única expressão Python gerada dinamicamente, e execé usado para exec ute gerado dinamicamente código Python apenas para os seus efeitos secundários.

evale exectem essas duas diferenças:

  1. evalaceita apenas uma única expressão , execpode tomar um bloco de código que tem comandos Python: loops, try: except:, classe função / método definitions e assim por diante.

    Uma expressão em Python é o que você pode ter como valor em uma atribuição de variável:

    a_variable = (anything you can put within these parentheses is an expression)
  2. eval retorna o valor da expressão fornecida, enquanto execignora o valor de retorno do seu código e sempre retorna None(no Python 2 é uma declaração e não pode ser usada como expressão, portanto, realmente não retorna nada).

Nas versões 1.0 - 2.7, exechavia uma declaração, porque o CPython precisava produzir um tipo diferente de objeto de código para funções que eram usadas execpara seus efeitos colaterais dentro da função.

No Python 3, execé uma função; seu uso não afeta o bytecode compilado da função em que é usado.


Assim basicamente:

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

O modo compilein 'exec'compila qualquer número de instruções em um bytecode que sempre retorna implicitamente None, enquanto no 'eval'modo compila uma única expressão no bytecode que retorna o valor dessa expressão.

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

No 'eval'modo (e, portanto, com a evalfunção se uma sequência for passada), isso compilegera uma exceção se o código-fonte contiver instruções ou qualquer outra coisa além de uma única expressão:

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

Na verdade, a declaração "eval aceita apenas uma expressão única" se aplica somente quando uma string (que contém o código-fonte Python ) é passada para eval. Em seguida, ele é compilado internamente no bytecode usando compile(source, '<string>', 'eval')É aqui que a diferença realmente vem.

Se um codeobjeto (que contém bytecode do Python ) é passado para execou eval, eles se comportam de forma idêntica , exceto pelo fato de execignorar o valor de retorno, ainda retornando Nonesempre. Portanto, é possível usar evalpara executar algo que possui instruções, se você apenas compileo introduzir no bytecode antes, em vez de passá-lo como uma string:

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

funciona sem problemas, mesmo que o código compilado contenha instruções. Ele ainda retorna None, porque esse é o valor de retorno do objeto de código retornado compile.

No 'eval'modo (e, portanto, com a evalfunção se uma sequência for passada), isso compilegera uma exceção se o código-fonte contiver instruções ou qualquer outra coisa além de uma única expressão:

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

A resposta mais longa, também conhecida como detalhes sangrentos

exec e eval

A execfunção (que era uma instrução no Python 2 ) é usada para executar uma instrução ou programa criado dinamicamente:

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

A evalfunção faz o mesmo para um único expressão , e retorna o valor da expressão:

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

exece evaltanto o programa aceitar / expressão para ser executado quer como um str, unicodeou bytescódigo de fonte de objectos que contém, ou como um codeobjecto que contém o Python código de bytes.

Se um str/ unicode/ bytescódigo-fonte contendo foi passado para exec, ele se comporta de forma equivalente a:

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

e evalcomporta-se da mesma forma equivalente a:

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

Como todas as expressões podem ser usadas como instruções no Python (elas são chamadas de Exprnós na gramática abstrata do Python ; o oposto não é verdadeiro), você sempre pode usar execse não precisar do valor de retorno. Ou seja, você pode usar um eval('my_func(42)')ou exec('my_func(42)'), a diferença é que evalretorna o valor retornado por my_funce o execdescarta:

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

Do 2, a apenas execaceita código-fonte que contém instruções, como def, for, while, import, ou class, a instrução de atribuição (aka a = 42), ou programas inteiros:

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

Ambos exece evalaceitar 2 argumentos posicionais adicionais - globalse locals- que são a variável global e local escopos que o código vê. Eles são padrão para globals()e locals()dentro do escopo que chamou execou eval, mas qualquer dicionário pode ser usado para globalse mappingpara locals(incluindo, é dictclaro). Eles podem ser usados ​​não apenas para restringir / modificar as variáveis ​​que o código vê, mas também costumam ser usados ​​para capturar as variáveis ​​que o execcódigo usado cria:

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

(Se você exibir o valor do todo g, seria muito mais longo, pois, execalém disso, evaladicione o módulo __builtins__interno às globais automaticamente, se estiver ausente).

No Python 2, a sintaxe oficial da execdeclaração é realmente exec code in globals, locals, como em

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

No entanto, a sintaxe alternativa exec(code, globals, locals)também sempre foi aceita (veja abaixo).

compile

O compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)built-in pode ser usado para acelerar invocações repetidas do mesmo código com execou evalcompilando a fonte em um codeobjeto de antemão. O modeparâmetro controla o tipo de fragmento de código que a compilefunção aceita e o tipo de bytecode que ela produz. As opções são 'eval', 'exec'e 'single':

  • 'eval'O modo espera uma expressão única e produzirá um bytecode que, quando executado, retornará o valor dessa expressão :

    >>> 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'aceita qualquer tipo de construção python, de expressões únicas a módulos inteiros de código, e as executa como se fossem instruções de nível superior do módulo. O objeto de código retorna 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'é uma forma limitada 'exec'que aceita um código-fonte contendo uma única instrução (ou várias instruções separadas por ;) se a última instrução for uma expressão, o bytecode resultante também imprimirá repro valor dessa expressão na saída padrão (!) .

    Um if- elif- elsecadeia, um laço com else, e trycom a sua except, elsee finallyblocos é considerado uma única instrução.

    Um fragmento de origem contendo 2 instruções de nível superior é um erro para o 'single'arquivo, exceto no Python 2, há um erro que às vezes permite várias instruções de nível superior no código; somente o primeiro é compilado; o resto é ignorado:

    No Python 2.7.8:

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

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

    Isso é muito útil para criar shells interativos em Python. No entanto, o valor da expressão não é retornado , mesmo se você tiver evalo código resultante.

Assim, a maior distinção exece de evalfato vem da compilefunção e de seus modos.


Além de compilar o código-fonte no bytecode, compilesuporta compilar árvores de sintaxe abstratas (analisar árvores do código Python) em codeobjetos; e código-fonte em árvores de sintaxe abstratas (ele ast.parseé escrito em Python e apenas chama compile(source, filename, mode, PyCF_ONLY_AST)); elas são usadas, por exemplo, para modificar o código-fonte rapidamente e também para a criação dinâmica de código, pois geralmente é mais fácil manipular o código como uma árvore de nós em vez de linhas de texto em casos complexos.


Embora evalapenas permita avaliar uma cadeia de caracteres que contenha uma única expressão, você pode evaluma declaração inteira ou mesmo um módulo inteiro que foi compileinserido no bytecode; isto é, com Python 2, printé uma declaração e não pode ser evalconduzida diretamente:

>>> 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-lo com 'exec'o modo em um codeobjeto e você pode eval -lo ; a evalfunção retornará 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 se olha para evale execcódigo fonte em CPython 3, isto é muito evidente; ambos chamam PyEval_EvalCodecom os mesmos argumentos, a única diferença é que execretorna explicitamenteNone .

Diferenças de sintaxe execentre Python 2 e Python 3

Uma das principais diferenças no Python 2 é que execé uma instrução e evalé uma função interna (ambas são funções internas do Python 3). É um fato bem conhecido que a sintaxe oficial do execPython 2 é exec code [in globals[, locals]].

Ao contrário maioria do Python 2 para 3 portabilidade guias parecem sugerir , a execdeclaração em CPython 2 pode ser também usado com sintaxe que parece exatamente como a execchamada de função em Python 3. A razão é que o Python 0.9.9 teve o exec(code, globals, locals)built em função! E essa função interna foi substituída pela execinstrução em algum lugar antes do lançamento do Python 1.0 .

Uma vez que era desejável para não quebrar a compatibilidade para trás com o Python 0.9.9, Guido van Rossum adicionado um corte compatibilidade em 1993 : se a codeera um tuplo de comprimento 2 ou 3, e globalse localsnão foram passados para a execdeclaração de outro modo, o codeque ser interpretado como se o 2º e o 3º elemento da tupla fossem globalse localsrespectivamente. O hack de compatibilidade não foi mencionado nem na documentação do Python 1.4 (a primeira versão disponível online) ; e, portanto, não era conhecido por muitos escritores dos guias e ferramentas de transporte, até que foi documentado novamente em novembro de 2012 :

A primeira expressão também pode ser uma tupla de comprimento 2 ou 3. Nesse caso, as partes opcionais devem ser omitidas. O formulário exec(expr, globals)é equivalente a exec expr in globals, enquanto o formulário exec(expr, globals, locals)é equivalente a exec expr in globals, locals. A forma de tupla execfornece compatibilidade com o Python 3, onde execé uma função e não uma instrução.

Sim, no CPython 2.7, é facilmente referido como sendo uma opção de compatibilidade com versões anteriores (por que confundir as pessoas sobre a existência de uma opção de compatibilidade com versões anteriores), quando ela realmente existia há duas décadas .

Portanto, while execé uma instrução no Python 1 e Python 2 e uma função interna no Python 3 e Python 0.9.9,

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

teve um comportamento idêntico em possivelmente todas as versões amplamente lançadas do Python de todos os tempos; e funciona em Jython 2.5.2, PyPy 2.3.1 (Python 2.7.6) e IronPython 2.6.1 também (parabéns a eles seguindo o comportamento não documentado do CPython de perto).

O que você não pode fazer no Pythons 1.0 - 2.7 com seu hack de compatibilidade é armazenar o valor de retorno execem uma variável:

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

(que também não seria útil no Python 3, como execsempre retorna None) ou passe uma referência para exec:

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

Qual padrão que alguém poderia realmente ter usado, embora improvável;

Ou use-o em uma lista de compreensão:

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

que é abuso de compreensão de lista (use um forloop!).


Foi [i for i in globals().values() if hasattr(i, '__call__')][0]uma afirmação ou expressão? Se era uma expressão, por que não posso usá-la @como decoradora?
Mario

é uma expressão. 42também é uma expressão e você não pode usá-la @como decoradora.
Antti Haapala

A sintaxe do decorador é decorator ::= "@" dotted_name ["(" [parameter_list [","]] ")"] NEWLINE; ou seja, você não pode usar expressões arbitrárias como decoradores, APENAS um identificador (talvez pontilhado), seguido por argumentos de chamada opcionais.
Antti Haapala

1
Nada do que possa ser colocado no lado direito de uma tarefa e ainda assim compilar é uma expressão. Por exemplo, a = b = cé uma afirmação perfeitamente válida, assim como seu lado direito b = c- que não é uma expressão.
Tom

194
  1. execnão é uma expressão: uma instrução no Python 2.xe uma função no Python 3.x. Ele compila e avalia imediatamente uma instrução ou conjunto de instruções contidas em uma string. Exemplo:

    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é uma função interna ( não uma instrução), que avalia uma expressão e retorna o valor que a expressão produz. Exemplo:

    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é uma versão de nível inferior de exece eval. Ele não executa ou avalia suas instruções ou expressões, mas retorna um objeto de código que pode fazê-lo. Os modos são os seguintes:

    1. compile(string, '', 'eval')retorna o objeto de código que teria sido executado se você tivesse feito eval(string). Observe que você não pode usar instruções neste modo; somente uma expressão (única) é válida.
    2. compile(string, '', 'exec')retorna o objeto de código que teria sido executado se você tivesse feito exec(string). Você pode usar qualquer número de instruções aqui.
    3. compile(string, '', 'single')é como o execmodo, mas ignorará tudo, exceto a primeira instrução. Observe que uma declaração if/ elsecom seus resultados é considerada uma declaração única.

40
No Python 3, exec()agora é de fato uma função.
precisa saber é o seguinte

2
Como (como você aponta), execé uma declaração na versão que você estava alvejando, é enganoso incluir esses parênteses e, se você tentar usar o in globals, locals, também com erros.
Mike Graham

2
O @MikeGraham exec suporta parênteses e funciona como invocação no Python 2 .
Antti Haapala

2
@AnttiHaapala na medida em que a atribuição 'suporta parênteses' porque você pode fazer x = (y)isso, isso pode ser verdade. Outra declaração que virou função é print; comparar o resultado de print(1, 2, 3)no pitão 2 e 3.
habnabit

1
@habnabit não é assim. Por favor, leia o final da minha resposta aqui e se surpreenda.
Antti Haapala

50

exec é para declaração e não retorna nada. eval é para expressão e retorna o valor da expressão.

expressão significa "alguma coisa", enquanto declaração significa "fazer alguma coisa".


9
O segundo parágrafo é uma simplificação que quase se torna uma mentira, uma expressão pode fazer algo muito se incluir uma chamada de função.
Antti Haapala
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.