¿Qué hace el eval () de Python?

306

En el libro que estoy leyendo en Python, sigue usando el código eval(input('blah'))

Leí la documentación y la entiendo, pero aún no veo cómo cambia la input()función.

¿Qué hace? Alguien puede explicar?

Billjk
fuente
44
La función Eval intenta ejecutar e interpretar la cadena (argumento) que se le pasa como código python. x = 1 print (eval ('x + 1')) La salida del código anterior será 2. La desventaja de este enfoque es que el usuario obtiene independencia de la escritura del código, lo que puede generar condiciones de caos. accediendo a muchas variables y métodos pasando parámetros globales y locales en la función eval.
ATIF IBAD KHAN

Respuestas:

276

La función eval permite que un programa Python ejecute código Python dentro de sí mismo.

Ejemplo de eval (shell interactivo):

>>> x = 1
>>> eval('x + 1')
2
>>> eval('x')
1
BYS2
fuente
25
jaja, ese fue un ejemplo trivial, pero puedes dejar que el usuario escriba un comando arbitrario y que Python lo ejecute. Entonces, puede hacer que el usuario escriba una cadena de comando y luego hacer que Python lo ejecute como código. Entonces, por ejemplo: eval ("__ import __ ('os'). Remove ('file')").
BYS2
6060
Parecerá inútil hasta que encuentre una necesidad para ello. Se utiliza en sitios como codepad.org para permitirle ejecutar scripts en un entorno de prueba. eval()también se puede usar para ejecutar código altamente dinámico, pero debe tener plena conciencia de los riesgos de seguridad y rendimiento antes de usarlo.
George Cummins
66
@GeorgeCummins, codepad.org no usa eval, ni podría hacer lo que hace eval.
Mike Graham
16
@GeorgeCummins: codepag.org ejecuta todo en una caja de arena: una cárcel chroot con controles de trazas en una máquina virtual para evitar que el código malicioso haga algo malo. Mucho más complicado que una simple evaluación. Además, eval es específico de Python. el teclado es compatible con varios idiomas.
FogleBird
44
@GeorgeCummins, codepad ejecuta un sistema muy complejo para ejecutar de forma segura programas arbitrarios. eval, además de ser inseguro, no puede ejecutar programas completos como lo hace el teclado porque solo puede evaluar una sola expresión.
Mike Graham
165

eval()interpreta una cadena como código. La razón por la que tanta gente le ha advertido sobre el uso de esto es porque un usuario puede usar esto como una opción para ejecutar código en la computadora. Si tiene eval(input())e osimportó, una persona podría escribir input() os.system('rm -R *')y eliminar todos sus archivos en su directorio de inicio. (Suponiendo que tiene un sistema unix). Usar eval()es un agujero de seguridad. Si necesita convertir cadenas a otros formatos, intente usar cosas que hagan eso, como int().

CoffeeRain
fuente
14
Quieres decir que usar evalcon input()es un agujero de seguridad. No ponga input()dentro de una declaración eval y estará bien.
Rohmer
19
@Rohmer, los datos inseguros pueden provenir de todas partes: solicitudes web, campos de entrada de formulario, lecturas de archivos, ... no solo de la entrada de la consola. Incluso si escribe los archivos usted mismo, puede contener entradas que originalmente provienen de una fuente no confiable. Entonces, evales un problema de seguridad en muchos casos.
sanderd17
3
ya que inputgeneralmente toma sus datos de la consola, el usuario podría simplemente salir del programa y escribir de rm -R *todos modos ...
cz
63

Muchas buenas respuestas aquí, pero ninguna describe el uso de eval()en el contexto de its globalsy localskwargs, es decir eval(expression, globals=None, locals=None)(ver documentos eval aquí ).

Se pueden usar para limitar las funciones que están disponibles a través de la evalfunción. Por ejemplo, si carga un nuevo intérprete de Python, el locals()y globals()será el mismo y se verá así:

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

Ciertamente, hay funciones dentro del builtinsmódulo que pueden causar un daño significativo a un sistema. Pero es posible bloquear cualquier cosa y todo lo que no queremos disponible. Pongamos un ejemplo. Digamos que queremos construir una lista para representar un dominio de los núcleos disponibles en un sistema. Para mí tengo 8 núcleos, así que querría una lista [1, 8].

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

Del mismo modo, todo __builtins__está disponible.

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

Okay. Entonces, allí vemos una función que queremos exponer y un ejemplo de un método (de muchos que pueden ser mucho más complejos) que no queremos exponer. Así que bloqueemos todo.

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

Hemos bloqueado efectivamente todas las __builtins__funciones y, como tal, hemos traído un nivel de protección a nuestro sistema. En este punto, podemos comenzar a agregar nuevamente las funciones que queremos exponer.

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

Ahora tenemos la cpu_countfunción disponible mientras bloqueamos todo lo que no queremos. En mi opinión, esto es súper poderoso y claramente desde el alcance de las otras respuestas, no es una implementación común. Hay numerosos usos para algo como esto y siempre que se maneje correctamente, personalmente creo que se evalpuede usar de manera segura con gran valor.

nótese bien

Otra cosa que es genial acerca de esto kwargses que puede comenzar a usar la taquigrafía para su código. Digamos que usa eval como parte de una tubería para ejecutar texto importado. El texto no necesita tener un código exacto, puede seguir algún formato de archivo de plantilla y aún ejecutar lo que desee. Por ejemplo:

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

En Python 2.x input(...)es equivalente a eval(raw_input(...)), en Python 3.x raw_inputse renombró input, lo que sospecho conduce a su confusión (probablemente estaba buscando la documentación inputen Python 2.x). Además, eval(input(...))funcionaría bien en Python 3.x, pero aumentaría a TypeErroren Python 2.

En este caso evalse utiliza para obligar a la cadena devuelta de inputuna expresión e interpretada. En general, esto se considera una mala práctica.

zeekay
fuente
O es un libro de Python 3.x, donde inputsignifica lo que raw_inputhizo en 2.x.
dan04
1
Sí, eso se me ocurrió después de escribir mi respuesta inicial, y ese es claramente el caso.
zeekay
6

Tal vez un ejemplo engañoso de leer una línea e interpretarla.

Prueba eval(input())y escribe "1+1": esto debería imprimirse 2. Eval evalúa expresiones.

hburde
fuente
¿Por qué debería escribirlo entre comillas? Input obtiene una cadena y la pasa a eval, no ejecuta el código, por lo que estaría bien si simplemente escribiera 1 + 1 ... ¿?
JC Rocamonde
El caso es que estás mezclando P2.xy 3.x. En Python 2 su código funciona, pero no tiene sentido evaluar dos veces. En python 3 no lo hace, y devuelve una cadena.
JC Rocamonde
6

eval()evalúa la cadena pasada como una expresión de Python y devuelve el resultado. Por ejemplo, eval("1 + 1")interpreta y ejecuta la expresión "1 + 1"y devuelve el resultado (2).

Una razón por la que puede confundirse es porque el código que citó implica un nivel de indirección. La llamada a la función interna (entrada) se ejecuta primero para que el usuario vea el mensaje "bla". Imaginemos que responden con "1 + 1" (citas agregadas para mayor claridad, no las escriba al ejecutar su programa), la función de entrada devuelve esa cadena, que luego se pasa a la función externa (eval) que interpreta la cadena y devuelve el resultado (2).

Lea más sobre eval aquí .

Marc Cohen
fuente
6

eval(), como su nombre indica, evalúa el argumento pasado.

raw_input()ahora está input()en versiones de python 3.x. Entonces, el ejemplo más común para el uso de eval()es su uso para proporcionar la funcionalidad que input()proporciona la versión 2.x de python. raw_input devolvió los datos ingresados ​​por el usuario como una cadena, mientras que input evaluó el valor de los datos ingresados ​​y los devolvió.

eval(input("bla bla"))replica la funcionalidad de input()in 2.x, es decir, de evaluar los datos ingresados ​​por el usuario.

En resumen: eval()evalúa los argumentos que se le pasan y, por lo tanto, eval('1 + 1')devuelve 2.

Rubal
fuente
6

Una de las aplicaciones útiles de eval()es evaluar las expresiones de python desde cadenas. Por ejemplo, cargar desde la representación de cadena de archivo del diccionario:

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

Léalo como una variable y edítelo:

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

Salida:

{'Greeting': 'Hello world'}
Nikolay Frick
fuente
77
¿Cómo responde esto a la pregunta que pregunta qué evalhace?
jkd
4

Llego tarde a responder esta pregunta, pero nadie parece dar una respuesta clara a la pregunta.

Si un usuario ingresa un valor numérico, input()devolverá una cadena.

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

Por lo tanto, eval()evaluará el valor devuelto (o expresión) que es una cadena y devolverá entero / flotante.

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

Por supuesto, esta es una mala práctica. int()o float()debe usarse en lugar de eval()en este caso.

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

Otra opción si desea limitar la cadena de evaluación a literales simples es usar ast.literal_eval(). Algunos ejemplos:

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}

De los documentos :

Evalúe de forma segura un nodo de expresión o una cadena que contenga un literal de Python o una pantalla de contenedor. La cadena o nodo proporcionado solo puede consistir en las siguientes estructuras literales de Python: cadenas, bytes, números, tuplas, listas, dictos, conjuntos, booleanos y Ninguno.

Esto se puede usar para evaluar de forma segura las cadenas que contienen valores de Python de fuentes no confiables sin la necesidad de analizar los valores uno mismo. Es no capaz de evaluar expresiones arbitrariamente complejas, por ejemplo, la participación de los operadores o indexación.

En cuanto a por qué es tan limitado, de la lista de correo :

Permitir expresiones de operador con literales es posible, pero mucho más complejo que la implementación actual. Una implementación simple no es segura: puede inducir el uso básicamente ilimitado de CPU y memoria sin esfuerzo (intente "9 ** 9 ** 9" o "[Ninguno] * 9 ** 9").

En cuanto a la utilidad, esta función es útil para "volver a leer" valores literales y contenedores en cadena por repr (). Esto se puede usar, por ejemplo, para la serialización en un formato similar pero más potente que JSON.

Brian Burns
fuente
1
ast.literal_evalno es compatible con operadores, contrario a su '1+1'ejemplo. No obstante, admite listas, números, cadenas, etc., por lo que es una buena alternativa para evalcasos de uso comunes .
benjimin
@benjimin oh, tienes razón: ¡es una peculiaridad que acepte 1 + 1! stackoverflow.com/questions/40584417/…
Brian Burns