versiones guia español descargar actualizar python eval

python - guia - qgis manual



¿Por qué usar ''eval'' es una mala práctica? (7)

Estoy usando la siguiente clase para almacenar fácilmente datos de mis canciones.

class Song: """The class to store the details of each song""" attsToStore=(''Name'', ''Artist'', ''Album'', ''Genre'', ''Location'') def __init__(self): for att in self.attsToStore: exec ''self.%s=None''%(att.lower()) in locals() def setDetail(self, key, val): if key in self.attsToStore: exec ''self.%s=val''%(key.lower()) in locals()

Siento que esto es mucho más extensible que escribir un bloque if/else . Sin embargo, eval parece ser una mala práctica y no es seguro de usar. Si es así, ¿alguien puede explicarme por qué y mostrarme una mejor manera de definir la clase anterior?


Cuando eval() se utiliza para procesar entradas provistas por el usuario, habilita al usuario a Drop-to-REPL proporcionando algo como esto:

"__import__(''code'').InteractiveConsole(locals=globals()).interact()"

Puede salirse con la suya, pero normalmente no quiere vectores para la ejecución de código arbitrario en sus aplicaciones.


En este caso, sí. En lugar de

exec ''self.Foo=val''

deberías usar la función builtin setattr :

setattr(self, ''Foo'', val)


Otros usuarios señalaron cómo se puede cambiar su código para que no dependa de eval ; Ofreceré un caso de uso legítimo para usar eval , uno que se encuentra incluso en CPython: testing .

Aquí hay un ejemplo que encontré en test_unary.py donde una prueba sobre si (+|-|~)b''a'' genera un TypeError :

def test_bad_types(self): for op in ''+'', ''-'', ''~'': self.assertRaises(TypeError, eval, op + "b''a''") self.assertRaises(TypeError, eval, op + "''a''")

El uso claramente no es una mala práctica aquí; usted define la entrada y simplemente observa el comportamiento. eval es útil para probar.

Eche un vistazo a esta búsqueda de eval , realizada en el repositorio CPython git; las pruebas con eval se usan mucho.


Sí lo es:

Hack usando Python:

>>> eval(input()) "__import__(''os'').listdir(''.'')" ........... ........... #dir listing ...........

El siguiente código enumerará todas las tareas que se ejecutan en una máquina con Windows.

>>> eval(input()) "__import__(''subprocess'').Popen([''tasklist''],stdout=__import__(''subprocess'').PIPE).communicate()[0]"

En Linux:

>>> eval(input()) "__import__(''subprocess'').Popen([''ps'', ''aux''],stdout=__import__(''subprocess'').PIPE).communicate()[0]"


Sí, usar eval es una mala práctica. Solo por nombrar algunas razones:

  1. Casi siempre hay una mejor manera de hacerlo
  2. Muy peligroso e inseguro
  3. Hace que la depuración sea difícil
  4. Lento

En su caso, puede usar setattr en setattr lugar:

class Song: """The class to store the details of each song""" attsToStore=(''Name'', ''Artist'', ''Album'', ''Genre'', ''Location'') def __init__(self): for att in self.attsToStore: setattr(self, att.lower(), None) def setDetail(self, key, val): if key in self.attsToStore: setattr(self, key.lower(), val)

EDITAR:

Hay algunos casos en los que debe usar eval o exec. Pero son raros. Usar eval en su caso es una mala práctica. Estoy haciendo hincapié en las malas prácticas porque eval y exec se utilizan con frecuencia en el lugar equivocado.

EDICION 2:

Parece que algunos no están de acuerdo con que eval sea "muy peligroso e inseguro" en el caso del OP. Eso podría ser cierto para este caso específico, pero no en general. La pregunta fue general y las razones que enumeré son verdaderas también para el caso general.

EDIT 3: Reordenado punto 1 y 4


Usar eval es débil, no es una práctica claramente mala .

  1. Viola el "Principio Fundamental del Software". Tu fuente no es la suma total de lo que es ejecutable. Además de su fuente, existen los argumentos para eval , que deben ser claramente entendidos. Por esta razón, es la herramienta de último recurso.

  2. Por lo general, es un signo de diseño irreflexivo. Rara vez hay una buena razón para el código fuente dinámico, construido sobre la marcha. Casi todo se puede hacer con delegación y otras técnicas de diseño OO.

  3. Lleva a una compilación relativamente lenta sobre pequeños fragmentos de código. Una sobrecarga que puede evitarse mediante el uso de mejores patrones de diseño.

Como nota a pie de página, en manos de sociópatas trastornados, puede no funcionar bien. Sin embargo, cuando se enfrentan con usuarios o administradores sociópatas trastornados, es mejor no darles Python interpretado en primer lugar. En manos del verdadero malvado, Python puede ser una responsabilidad; eval no aumenta el riesgo en absoluto.


Vale la pena señalar que para el problema específico en cuestión, existen varias alternativas para usar eval :

El más simple, como se señaló, es usar setattr :

def __init__(self): for name in attsToStore: setattr(self, name, None)

Un enfoque menos obvio es actualizar el objeto __dict__ objeto directamente. Si todo lo que quiere hacer es inicializar los atributos a None , entonces esto es menos directo que el anterior. Pero considera esto:

def __init__(self, **kwargs): for name in self.attsToStore: self.__dict__[name] = kwargs.get(name, None)

Esto le permite pasar argumentos de palabra clave al constructor, por ejemplo:

s = Song(name=''History'', artist=''The Verve'')

También le permite hacer que su uso de los locals() más explícito, por ejemplo:

s = Song(**locals())

... y, si realmente quiere asignar None a los atributos cuyos nombres se encuentran en locals() :

s = Song(**dict([(k, None) for k in locals().keys()]))

Otro enfoque para proporcionar un objeto con valores predeterminados para una lista de atributos es definir el método __getattr__ la clase:

def __getattr__(self, name): if name in self.attsToStore: return None raise NameError, name

Se llama a este método cuando el atributo nombrado no se encuentra de la manera normal. Este enfoque es algo menos sencillo que simplemente establecer los atributos en el constructor o actualizar el __dict__ , pero tiene el mérito de no crear realmente el atributo a menos que exista, lo que puede reducir considerablemente el uso de la memoria de la clase.

El objetivo de todo esto: hay muchas razones, en general, para evitar la eval : el problema de seguridad de ejecutar un código que no se controla, el problema práctico del código que no se puede depurar, etc. Pero una cosa aún más importante la razón es que, en general, no es necesario que lo use. Python expone tanto de sus mecanismos internos al programador que rara vez necesita escribir código que escriba código.