2.34. Código dinâmico¶
Três built-ins recebem uma string de código-fonte Python e executam-na: eval(), exec() e compile(). Em conjunto permitem que o código construa e execute mais código em tempo de execução – o que é, ocasionalmente, a ferramenta certa, e muito mais frequentemente uma fonte de erros e falhas de segurança.
Aviso
Estas funções executam Python arbitrário. Passar input do utilizador para eval ou exec – uma string de um ficheiro que o utilizador pode editar, um payload recebido pela rede, um valor introduzido numa prompt REPL – permite que esse input faça tudo o que o script chamador poderia fazer, incluindo eliminar todos os ficheiros do dispositivo. Utilize-as deliberadamente, nunca em código que corra automaticamente, e nunca com dados que não controla.
2.34.1. eval¶
eval() executa uma única expressão e devolve o seu valor:
>>> eval("3 * 7")
21
>>> name = "OpenMV"
>>> eval("name.lower()")
'openmv'
A expressão vê os globais e locais do chamador por omissão, razão pela qual name é resolvido no segundo exemplo. Passar dicionários explícitos permite isolar a avaliação numa sandbox:
eval("a + b", {"__builtins__": None}, {"a": 1, "b": 2})
Mesmo em sandbox, eval é perigoso. Existem técnicas bem conhecidas para escapar a essas sandboxes; não confie apenas no truque do __builtins__ vazio para input não confiável.
2.34.2. exec¶
exec() executa um bloco de código em vez de uma única expressão – instruções, definições de funções, ciclos – e devolve None:
exec("for i in range(3): print(i)")
Saída:
0
1
2
O bloco pode definir nomes que ficam disponíveis depois, com algumas ressalvas sobre âmbito local vs global. exec dentro de uma função raramente se comporta como o autor espera; se precisar, execute-o ao nível do módulo.
2.34.3. compile¶
compile() transforma uma string de código-fonte num objeto de código que pode ser passado a eval() ou exec() posteriormente. Use-o quando a mesma fonte vai ser executada muitas vezes – o parsing ocorre uma vez, a execução é mais rápida:
expr = compile("x * x", "<expr>", "eval")
for x in range(5):
print(eval(expr))
Saída:
0
1
4
9
16
O segundo argumento é um rótulo que aparece nos tracebacks se o código levantar uma exceção. O terceiro argumento é "eval" para uma única expressão, "exec" para um bloco, ou "single" para uma instrução no estilo interativo que imprime o seu resultado.
2.34.4. Quando recorrer a estas funções¶
Quase nunca. A maioria dos casos de uso que os principiantes imaginam tem alternativas mais seguras:
Ler um ficheiro de configuração. Use
json– dados estruturados, sem execução.Avaliar um valor numérico introduzido pelo utilizador. Use
int()/float()para fazer o parsing e depois aritmética. Se o utilizador precisar mesmo de introduzir uma fórmula, use um parser de expressões pequeno, nãoeval.
Quando precisar mesmo de eval / exec / compile, isole o ponto de chamada, registe a string exata que está prestes a ser executada e trate a fonte como o elemento mais suspeito do seu código.