Python’s eval(): la función más poderosa que nunca deberías usar.
eval()
es una función simple y poderosa integrada en el intérprete de Python. Como su nombre lo indica, la función *evalúa* cualquier expresión que se le pase. Este argumento se pasa como una cadena, con argumentos opcionales para globals
y locals
dónde globals
es un diccionario y locals
es cualquier objeto de mapeo, generalmente un diccionario. Esto significa con eval()
, puede evaluar expresiones arbitrarias de python almacenadas como cadenas. Más detalles sobre eval()
, globals
y locals
siempre se puede encontrar en la documentación oficial de Python.
Desde eval()
es una función tan poderosa, debemos recordar la lección número uno que vale la pena aprender de Spider-man: un gran poder conlleva una gran responsabilidad.
En la práctica, la recomendación más clara es que nunca, nunca, se debe utilizar eval()
. Al cumplir con esta recomendación, se asegura de cumplir con las mejores prácticas de codificación.
- Primero, como práctica recomendada, el comportamiento de su programa debe ser capturado por el propio código, una vez que comience a usar
eval()
el comportamiento del programa se vuelve mucho menos predecible.
En segundo lugar, aceptar entradas no confiables para eval()
puede permitir la ejecución de código arbitrario por parte de usuarios malintencionados. Por ejemplo, un usuario malicioso privilegiado podría pasar la cadena '__import__('subprocess').getoutput('rm -rf /')'
para eliminar todo el sistema de archivos, o use una utilidad como netcat para abrir una puerta trasera en la máquina.
En casi todos los casos, uno puede y debe usar Python nativo, o si debe ejecutar cadenas, aprovechar funciones y clases desde el [subprocess
module].
Consideremos primero el ejemplo presentado en la documentación oficial:
En este caso, eval()
regresará 2
. Funcionalmente, esto es equivalente a ejecutar la expresión x + 1
.
Entonces, imaginemos que queremos una función que tome un solo argumento, x
y devolver el valor x + 1
. Esto, por supuesto, es preferible, porque es mucho más seguro que tomar la entrada arbitraria del usuario para eval()
.
def increment(x):
return x + 1
Ejemplo de uso
Ahora, consideremos un ejemplo:
# Perimeter of Square
def calculatePerimeter(l):
return 4*l
# Area of Square
def calculateArea(l):
return l*l
expression = input("Type a function: ")
for l in range(1, 5):
if (expression == 'calculatePerimeter(l)'):
print("If length is ", l, ", Perimeter = ", eval(expression))
elif (expression == 'calculateArea(l)'):
print("If length is ", l, ", Area = ", eval(expression))
else:
print('Wrong Function')
break
Este es un uso mucho más complejo de eval()
, ¡y parece un uso bastante convincente! Sin embargo, incluso con nuestra verificación de expresiones, pasar la entrada del usuario directamente a eval()
puede ser un riesgo significativo para la seguridad y es un mal hábito.
Consideremos cómo podríamos refactorizar esto *no* para usar eval()
.
# Perimeter of Square
def calculatePerimeter(l):
return 4*l
# Area of Square
def calculateArea(l):
return l*l
expression = input("Would you like to compute the area or the perimeter? ")
for l in range(1, 5):
if (expression == 'perimeter'):
print(f"If length is {l}, Perimeter = {calculatePerimeter(l)}")
elif (expression == 'area'):
print(f"If length is {l}, Area = {calculateArea(l)}")
else:
print('Wrong Function')
break
Simplemente reemplazando la llamada a eval
con una llamada de cadena de formato a la función en sí y reformulando la pregunta, evitamos adquirir el hábito de llamar a funciones peligrosas como eval()
.
Limitar la ejecución con globals
y locals
¿Qué pasa si estás atascado con eval()
¿por alguna razón? ¡O tal vez no tengas miedo y estés feliz de ignorar las advertencias de no usarlo! Bueno, en esos casos, es importante entender cómo globals
y locals
trabajar.
Como mencionamos (¡y como puede encontrar en la documentación!), globals
debe ser un pitón dict()
objeto. Este diccionario es análogo al devuelto por el globals()
función integrada y consta de global
variables configuradas en el programa asignadas a su valor. Al restringir el acceso a un subconjunto del diccionario de variables globales, la funcionalidad de eval()
se reduce ligeramente.
Similarmente, locals
es un mapeo de variables de alcance local, generalmente un diccionario, que se puede especificar para restringir la llamada de eval()
a un conjunto más pequeño de variables locales. Al pasar diccionarios vacíos a ambos globals
y locals
, eval()
no puede acceder a los valores almacenados en otras partes del programa, algo que ciertamente reduce su poder.
Conclusión
Aunque usando eval()
se desaconseja, es una función importante de entender. Es una de las pocas funciones integradas que no necesita importarse desde un módulo o la biblioteca estándar y, para bien o para mal, puede encontrarla cuando se trata de código heredado o mal mantenido.
En la mayoría de los casos, ser cuidadoso con la creación de su código puede permitirle solucionarlo, pero en los casos en que debe usarse, validar la entrada a la función y usar restricciones globals
y locals
puede hacerlo más tolerable. si vas a eval()
¡hazlo con responsabilidad!
¿Busca ampliar su conocimiento de Python? La programación de Udacity para la ciencia de datos con Python Nanodegree le enseñará los fundamentos de Python para ayudarlo a prepararse para una carrera en ciencia de datos. Si ya está familiarizado con los conceptos básicos de Python, tome el programa Intermediate Python Nanodegree de Udacity para obtener habilidades de programación de nivel profesional.