Eu estive olhando para avaliação dinâmica de código Python, e se deparar com o eval()
e compile()
funções, e o exec
comunicado.
Alguém pode explicar a diferença entre eval
e exec
, e como os diferentes modos de compile()
adaptação se encaixam?
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.
eval
e exec
tem essas duas diferenças:
eval
aceita apenas uma única expressão , exec
pode tomar um bloco de código que tem comandos Python: loops, try: except:
, class
e função / método def
initions 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)
eval
retorna o valor da expressão fornecida, enquanto exec
ignora 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, exec
havia uma declaração, porque o CPython precisava produzir um tipo diferente de objeto de código para funções que eram usadas exec
para 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 compile
in '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 eval
função se uma sequência for passada), isso compile
gera 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 code
objeto (que contém bytecode do Python ) é passado para exec
ou eval
, eles se comportam de forma idêntica , exceto pelo fato de exec
ignorar o valor de retorno, ainda retornando None
sempre. Portanto, é possível usar eval
para executar algo que possui instruções, se você apenas compile
o 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 eval
função se uma sequência for passada), isso compile
gera 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
exec
e eval
A exec
funçã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 eval
funçã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
exec
e eval
tanto o programa aceitar / expressão para ser executado quer como um str
, unicode
ou bytes
código de fonte de objectos que contém, ou como um code
objecto que contém o Python código de bytes.
Se um str
/ unicode
/ bytes
código-fonte contendo foi passado para exec
, ele se comporta de forma equivalente a:
exec(compile(source, '<string>', 'exec'))
e eval
comporta-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 Expr
nós na gramática abstrata do Python ; o oposto não é verdadeiro), você sempre pode usar exec
se 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 eval
retorna o valor retornado por my_func
e o exec
descarta:
>>> 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 exec
aceita 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 exec
e eval
aceitar 2 argumentos posicionais adicionais - globals
e 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 exec
ou eval
, mas qualquer dicionário pode ser usado para globals
e mapping
para locals
(incluindo, é dict
claro). 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 exec
có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, exec
além disso, eval
adicione o módulo __builtins__
interno às globais automaticamente, se estiver ausente).
No Python 2, a sintaxe oficial da exec
declaraçã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 exec
ou eval
compilando a fonte em um code
objeto de antemão. O mode
parâmetro controla o tipo de fragmento de código que a compile
funçã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á repr
o valor dessa expressão na saída padrão (!) .
Um if
- elif
- else
cadeia, um laço com else
, e try
com a sua except
, else
e finally
blocos é 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 eval
o código resultante.
Assim, a maior distinção exec
e de eval
fato vem da compile
função e de seus modos.
Além de compilar o código-fonte no bytecode, compile
suporta compilar árvores de sintaxe abstratas (analisar árvores do código Python) em code
objetos; 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 eval
apenas permita avaliar uma cadeia de caracteres que contenha uma única expressão, você pode eval
uma declaração inteira ou mesmo um módulo inteiro que foi compile
inserido no bytecode; isto é, com Python 2, print
é uma declaração e não pode ser eval
conduzida 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 code
objeto e você pode eval
-lo ; a eval
funçã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 eval
e exec
código fonte em CPython 3, isto é muito evidente; ambos chamam PyEval_EvalCode
com os mesmos argumentos, a única diferença é que exec
retorna explicitamenteNone
.
exec
entre Python 2 e Python 3Uma 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 exec
Python 2 é exec code [in globals[, locals]]
.
Ao contrário maioria do Python 2 para 3 portabilidade guias parecem sugerir , a exec
declaração em CPython 2 pode ser também usado com sintaxe que parece exatamente como a exec
chamada 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 exec
instruçã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 code
era um tuplo de comprimento 2 ou 3, e globals
e locals
não foram passados para a exec
declaração de outro modo, o code
que ser interpretado como se o 2º e o 3º elemento da tupla fossem globals
e locals
respectivamente. 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 aexec expr in globals
, enquanto o formulárioexec(expr, globals, locals)
é equivalente aexec expr in globals, locals
. A forma de tuplaexec
fornece compatibilidade com o Python 3, ondeexec
é 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 exec
em 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 exec
sempre 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 for
loop!).
[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?42
também é uma expressão e você não pode usá-la@
como decoradora.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.a = b = c
é uma afirmação perfeitamente válida, assim como seu lado direitob = c
- que não é uma expressão.exec
nã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: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:compile
é uma versão de nível inferior deexec
eeval
. 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:compile(string, '', 'eval')
retorna o objeto de código que teria sido executado se você tivesse feitoeval(string)
. Observe que você não pode usar instruções neste modo; somente uma expressão (única) é válida.compile(string, '', 'exec')
retorna o objeto de código que teria sido executado se você tivesse feitoexec(string)
. Você pode usar qualquer número de instruções aqui.compile(string, '', 'single')
é como oexec
modo, mas ignorará tudo, exceto a primeira instrução. Observe que uma declaraçãoif
/else
com seus resultados é considerada uma declaração única.fonte
exec()
agora é de fato uma função.exec
é uma declaração na versão que você estava alvejando, é enganoso incluir esses parênteses e, se você tentar usar oin globals, locals
, também com erros.exec
suporta parênteses e funciona como invocação no Python 2 .x = (y)
isso, isso pode ser verdade. Outra declaração que virou função éprint
; comparar o resultado deprint(1, 2, 3)
no pitão 2 e 3.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".
fonte