O que eval () do Python faz?

306

No livro que estou lendo sobre Python, ele continua usando o código eval(input('blah'))

Eu li a documentação e a compreendo, mas ainda não vejo como isso altera a input()função.

O que isso faz? Alguém pode explicar?

Billjk
fonte
4
A função Eval tenta executar e interpretar a string (argumento) passada a ela como código python. x = 1 print (eval ('x + 1')) A saída do código acima será 2. A desvantagem dessa abordagem é que, o usuário obtém independência da escrita do código, o que pode resultar em condições perigosas. acessando muitas variáveis ​​e métodos passando parâmetro global e local na função eval.
ATIF IBAD KHAN

Respostas:

276

A função eval permite que um programa Python execute o código Python dentro de si.

exemplo eval (shell interativo):

>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1
BYS2
fonte
25
haha, esse foi um exemplo trivial, mas você pode deixar o usuário digitar um comando arbitrário e fazer com que o python o execute. Assim, você pode fazer com que o usuário digite uma string de comando e, em seguida, faça com que o python a execute como código. Por exemplo, eval ("__ import __ ('os'). Remove ('file')").
BYS2
60
Vai parecer inútil até que você encontre uma necessidade. É usado em sites como o codepad.org para permitir a execução de scripts em um ambiente de teste. eval()também pode ser usado para executar código altamente dinâmico, mas você deve se conscientizar completamente dos riscos de segurança e desempenho antes de usá-lo.
George Cummins
6
@GeorgeCummins, codepad.org não usa eval, nem poderia fazer o que faz eval.
Mike Graham
16
@GeorgeCummins: codepag.org executa tudo em uma sandbox: uma cadeia chroot com ptrace faz check-in em uma máquina virtual para impedir que códigos maliciosos façam algo ruim. Muito mais complicado do que uma avaliação simples. Além disso, eval é específico do Python. O codepad suporta vários idiomas.
FogleBird 21/02/12
4
@GeorgeCummins, o codepad executa um sistema muito complexo para executar programas arbitrários com segurança. eval, além de inseguro, não pode executar programas inteiros como o codepad, porque só pode avaliar uma única expressão.
Mike Graham
165

eval()interpreta uma string como código. A razão pela qual tantas pessoas o alertaram sobre o uso disso é porque um usuário pode usá-lo como uma opção para executar código no computador. Se você tem eval(input())e osimportados, uma pessoa poderia digitar em input() os.system('rm -R *')que iria apagar todos os seus arquivos em seu diretório home. (Supondo que você tenha um sistema unix). Usar eval()é uma falha de segurança. Se você precisar converter seqüências de caracteres para outros formatos, tente usar coisas que fazem isso, como int().

CoffeeRain
fonte
14
Você quer dizer que usar evalwith input()é uma falha de segurança. Não insira input()uma declaração de avaliação e você ficará bem.
Rohmer 02/04
19
@Rohmer, dados inseguros podem vir de qualquer lugar: solicitações da Web, campos de entrada de formulário, leituras de arquivos, ... não apenas da entrada do console. Mesmo se você mesmo escrever os arquivos, ele ainda poderá conter entradas que vieram originalmente de uma fonte não confiável. O mesmo evalocorre com muitos problemas de segurança.
sanderd17
3
desde que inputgeralmente leva seus dados a partir do console o usuário poderia simplesmente sair do programa e digite rm -R *qualquer maneira ...
CZ
63

Muitas respostas boas aqui, mas nenhuma descreve o uso de eval()no contexto its globalse localskwargs, isto é eval(expression, globals=None, locals=None)(consulte a documentação eval aqui ).

Eles podem ser usados ​​para limitar as funções disponíveis através da evalfunção. Por exemplo, se você carregar um intérprete python novo, o locals()e globals()será o mesmo e terá algo parecido com isto:

>>>globals()
{'__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__doc__': None,
 '__spec__': None, '__builtins__': <module 'builtins' (built-in)>,
 '__package__': None, '__name__': '__main__'}

Certamente, existem funções no builtinsmódulo que podem causar danos significativos a um sistema. Mas é possível bloquear tudo e qualquer coisa que não queremos disponível. Vamos dar um exemplo. Digamos que queremos construir uma lista para representar um domínio dos núcleos disponíveis em um sistema. Para mim eu tenho 8 núcleos, então eu gostaria de uma lista [1, 8].

>>>from os import cpu_count
>>>eval('[1, cpu_count()]')
[1, 8]

Da mesma forma, tudo __builtins__está disponível.

>>>eval('abs(-1)')
1

Está bem. Então, vemos uma função que queremos expor e um exemplo de um método (de muitos que pode ser muito mais complexo) que não queremos que seja exposto. Então, vamos bloquear tudo.

>>>eval('[1, cpu_count()]', {'__builtins__':None}, {})
TypeError: 'NoneType' object is not subscriptable

Bloqueamos efetivamente todas as __builtins__funções e, como tal, trouxemos um nível de proteção ao nosso sistema. Nesse ponto, podemos começar a adicionar novamente funções que queremos que sejam expostas.

>>>from os import cpu_count
>>>exposed_methods = {'cpu_count': cpu_count}
>>>eval('cpu_count()', {'__builtins__':None}, exposed_methods)
8
>>>eval('abs(cpu_count())', {'__builtins__':None}, exposed_methods)
TypeError: 'NoneType' object is not subscriptable

Agora temos a cpu_countfunção disponível enquanto ainda bloqueamos tudo o que não queremos. Na minha opinião, isso é super poderoso e claramente do escopo das outras respostas, não de uma implementação comum. Existem inúmeros usos para algo assim e, desde que seja manuseado corretamente, eu pessoalmente acho que evalpode ser usado com segurança por um grande valor.

NB

Outra coisa interessante sobre isso kwargsé que você pode começar a usar uma abreviação para seu código. Digamos que você use eval como parte de um pipeline para executar algum texto importado. O texto não precisa ter o código exato, ele pode seguir algum formato de arquivo de modelo e ainda executar o que você desejar. Por exemplo:

>>>from os import cpu_count
>>>eval('[1,cores]', {'__builtins__': None}, {'cores': cpu_count()})
[1, 8]
Grr
fonte
29

No Python 2.x input(...)é equivalente eval(raw_input(...)), no Python 3.x raw_inputfoi renomeado input, o que eu suspeito levar à sua confusão (você provavelmente estava procurando a documentação inputdo Python 2.x). Além disso, eval(input(...))funcionaria bem no Python 3.x, mas aumentaria um TypeErrorno Python 2.

Nesse caso, evalé usado para coagir a sequência retornada inputem uma expressão e interpretada. Geralmente, isso é considerado uma má prática.

zeekay
fonte
Ou é um livro Python 3.x, onde inputsignifica o que raw_inputfez no 2.x.
dan04
1
Sim, isso me ocorreu depois que escrevi minha resposta inicial, e esse é claramente o caso.
Zeekay
6

Talvez um exemplo enganoso de ler e interpretar uma linha.

Experimente eval(input())e digite "1+1"- isso deve ser impresso 2. Eval avalia expressões.

hburde
fonte
Por que devo digitá-lo entre aspas? A entrada está recebendo uma string e a passa para avaliação, não executando o código, então eu ficaria bem se simplesmente digitasse 1 + 1 ... ¿?
JC Rocamonde
O problema é que você está misturando P2.xe 3.x. No Python 2, seu código funciona, mas não faz sentido avaliar duas vezes. No python 3, ele não retorna e retorna uma string.
JC Rocamonde
6

eval()avalia a cadeia passada como uma expressão Python e retorna o resultado. Por exemplo, eval("1 + 1")interpreta e executa a expressão "1 + 1"e retorna o resultado (2).

Uma razão pela qual você pode estar confuso é porque o código que você citou envolve um nível de indireção. A chamada de função interna (entrada) é executada primeiro para que o usuário veja o prompt "blah". Vamos imaginar que eles respondem com "1 + 1" (aspas adicionadas para maior clareza, não as digitam ao executar o programa), a função de entrada retorna essa string, que é então passada para a função externa (eval) que interpreta a string e retorna o resultado (2).

Leia mais sobre eval aqui .

Marc Cohen
fonte
6

eval(), como o nome sugere, avalia o argumento passado.

raw_input()está agora input()nas versões python 3.x. Portanto, o exemplo mais comumente encontrado para o uso de eval()é seu uso para fornecer a funcionalidade input()fornecida na versão 2.x do python. raw_input retornou os dados inseridos pelo usuário como uma sequência, enquanto a entrada avaliou o valor dos dados inseridos e os retornou.

eval(input("bla bla"))assim, replica a funcionalidade do input()2.x, isto é, da avaliação dos dados inseridos pelo usuário.

Em resumo: eval()avalia os argumentos passados ​​para ele e, portanto, eval('1 + 1')retornou 2.

Rubal
fonte
6

Uma das aplicações úteis de eval()é avaliar expressões python da string. Por exemplo, carregar da representação de sequência de arquivos do dicionário:

running_params = {"Greeting":"Hello "}
fout = open("params.dat",'w')
fout.write(repr(running_params))
fout.close()

Leia-o como uma variável e edite-o:

fin = open("params.dat",'r')
diction=eval(fin.read())
diction["Greeting"]+="world"
fin.close()
print diction

Resultado:

{'Greeting': 'Hello world'}
Nikolay Frick
fonte
7
Como isso responde à pergunta que pergunta o que evalfaz?
Jkd 28/03
4

Estou atrasado para responder a essa pergunta, mas ninguém parece dar uma resposta clara à pergunta.

Se um usuário digitar um valor numérico, input()retornará uma sequência.

>>> input('Enter a number: ')
Enter a number: 3
>>> '3'
>>> input('Enter a number: ')
Enter a number: 1+1
'1+1'

Portanto, eval()avaliaremos o valor retornado (ou expressão) que é uma string e retornará um número inteiro / float.

>>> eval(input('Enter a number: '))
Enter a number: 1+1
2
>>> 
>>> eval(input('Enter a number: '))
Enter a number: 3.14
3.14

É claro que essa é uma prática ruim. int()ou float()deve ser usado em vez de eval()neste caso.

>>> float(input('Enter a number: '))
Enter a number: 3.14
3.14
Calvin Kim
fonte
3

Outra opção se você deseja limitar a cadeia de avaliação a literais simples é usar ast.literal_eval(). Alguns exemplos:

import ast

# print(ast.literal_eval(''))          # SyntaxError: unexpected EOF while parsing
# print(ast.literal_eval('a'))         # ValueError: malformed node or string
# print(ast.literal_eval('import os')) # SyntaxError: invalid syntax
# print(ast.literal_eval('1+1'))       # 2: but only works due to a quirk in parser
# print(ast.literal_eval('1*1'))       # ValueError: malformed node or string
print(ast.literal_eval("{'a':1}"))     # {'a':1}

Dos documentos :

Avalie com segurança um nó de expressão ou uma sequência que contém um literal Python ou exibição de contêiner. A cadeia ou nó fornecido pode apenas consistir nas seguintes estruturas literais do Python: cadeias, bytes, números, tuplas, listas, dictos, conjuntos, booleanos e Nenhum.

Isso pode ser usado para avaliar com segurança seqüências de caracteres contendo valores Python de fontes não confiáveis, sem a necessidade de analisar os próprios valores. É não capaz de avaliar expressões complexas arbitrariamente, por exemplo envolvendo operadores ou indexação.

Por que é tão limitado, na lista de discussão :

Permitir expressões de operador com literais é possível, mas muito mais complexo que a implementação atual. Uma implementação simples não é segura: você pode induzir basicamente o uso ilimitado da CPU e da memória sem esforço (tente "9 ** 9 ** 9" ou "[None] * 9 ** 9").

Quanto à utilidade, esta função é útil para "ler de volta" valores literais e contêineres, conforme especificado por repr (). Por exemplo, isso pode ser usado para serialização em um formato semelhante, mas mais poderoso que o JSON.

Brian Burns
fonte
1
ast.literal_evalnão suporta operadores, ao contrário do seu '1+1'exemplo. No entanto, ele suporta listas, números, seqüências de caracteres etc, e, portanto, é uma boa alternativa para evalcasos de uso comuns .
benjimin
@benjimin oh você está certo - é apenas uma peculiaridade que aceita 1 + 1! stackoverflow.com/questions/40584417/…
Brian Burns