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 if/else
bloque. Sin embargo, eval
parece ser considerado una mala práctica e inseguro de usar. Si es así, ¿alguien puede explicarme por qué y mostrarme una mejor manera de definir la clase anterior?
exec/eval
y aún no lo sabíasetattr
?Respuestas:
Sí, usar eval es una mala práctica. Solo por nombrar algunas razones:
En su caso, puede usar setattr en su lugar:
EDITAR:
Hay algunos casos en los que tiene que usar eval o exec. Pero son raros. Usar eval en su caso es una mala práctica con seguridad. Estoy haciendo hincapié en las malas prácticas porque eval y exec se usan con frecuencia en el lugar equivocado.
EDITAR 2:
Parece un poco en desacuerdo que eval sea 'muy peligroso e inseguro' en el caso de OP. Eso podría ser cierto para este caso específico, pero no en general. La pregunta era general y las razones que enumeré también son ciertas para el caso general.
EDITAR 3: reordenó los puntos 1 y 4
fuente
eval
no tiene nada que ver el uno con el otro. Una aplicación que está fundamentalmente mal diseñada está fundamentalmente mal diseñada.eval
La causa del mal diseño no es más que la división por cero o el intento de importar un módulo que se sabe que no existe.eval
No es inseguro. Las aplicaciones son inseguras.calc
, y para agregar números se ejecutaprint(eval("{} + {}".format(n1, n2)))
y sale. Ahora distribuyes este programa con algunos SO. Luego, alguien crea un script bash que toma algunos números de un sitio web y los agrega usandocalc
. ¿auge?Usar
eval
es débil, no es una práctica claramente mala .Viola el "Principio fundamental del software". Su fuente no es la suma total de lo que es ejecutable. Además de su fuente, existen argumentos
eval
que deben entenderse claramente. Por esta razón, es la herramienta de último recurso.Suele ser 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 cualquier cosa se puede hacer con delegación y otras técnicas de diseño OO.
Conduce a una compilación relativamente lenta sobre la marcha de pequeñas piezas de código. Una sobrecarga que puede evitarse utilizando mejores patrones de diseño.
Como nota al pie, en manos de sociópatas trastornados, puede que no funcione bien. Sin embargo, cuando se enfrentan a usuarios o administradores sociópatas trastornados, es mejor no darles Python interpretado en primer lugar. En manos de los verdaderamente malvados, Python puede ser una responsabilidad;
eval
no aumenta el riesgo en absoluto.fuente
eval
es algún tipo de "vulnerabilidad de seguridad". Como si Python, en sí mismo, no fuera solo un montón de fuentes interpretadas que cualquiera podría modificar. Cuando te enfrentas a la "evaluación es un agujero de seguridad", solo puedes asumir que es un agujero de seguridad en manos de sociópatas. Los programadores ordinarios simplemente modifican la fuente Python existente y causan sus problemas directamente. No indirectamente a través de laeval
magia.while True: pass
, sería difícil de limpiar con algún tipo de escape.eval()
, ya que es una cadena. El código del "mundo exterior" no se puede desinfectar. Las cuerdas del mundo exterior son solo cuerdas. No tengo claro de qué estás hablando. Tal vez debería proporcionar una publicación de blog más completa y un enlace a ella aquí.En este caso, si. En vez de
deberías usar la función incorporada
setattr
:fuente
Sí lo es:
Hackear usando Python:
El siguiente código enumerará todas las tareas que se ejecutan en una máquina con Windows.
En Linux:
fuente
Vale la pena señalar que para el problema específico en cuestión, hay varias alternativas para usar
eval
:Lo más simple, como se señaló, es usar
setattr
:Un enfoque menos obvio es actualizar el objeto del
__dict__
objeto directamente. Si todo lo que quiere hacer es inicializar los atributosNone
, entonces esto es menos sencillo que lo anterior. Pero considera esto:Esto le permite pasar argumentos de palabras clave al constructor, por ejemplo:
También le permite hacer su uso
locals()
más explícito, por ejemplo:... y, si realmente desea asignar
None
a los atributos cuyos nombres se encuentran enlocals()
:Otro enfoque para proporcionar un objeto con valores predeterminados para una lista de atributos es definir el
__getattr__
método de la clase :Este método recibe una llamada cuando el atributo nombrado no se encuentra de la manera normal. Este enfoque es algo menos directo 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 sustancialmente el uso de memoria de la clase.El punto de todo esto: hay muchas razones, en general, para evitar
eval
: el problema de seguridad de ejecutar código que no controlas, el problema práctico de código que no puedes depurar, etc. Pero una razón aún más importante es que generalmente no necesitas usarlo. Python expone gran parte de sus mecanismos internos al programador que rara vez realmente necesita escribir código que escriba código.fuente
__dict__
directamente, dele al objeto un objeto de diccionario real, ya sea por herencia o como un atributo.__setattr__
anulación, lo que podría conducir a resultados inesperados.setattr()
no tiene este problemaOtros usuarios señalaron cómo se puede cambiar su código para no depender de él
eval
; Ofreceré un caso de uso legítimo para usareval
, uno que se encuentra incluso en CPython: pruebas .Aquí hay un ejemplo que encontré en
test_unary.py
donde una prueba sobre si(+|-|~)b'a'
plantea unTypeError
:El uso claramente no es una mala práctica aquí; Usted define la entrada y simplemente observa el comportamiento.
eval
Es útil para las pruebas.Echar un vistazo a esta búsqueda para
eval
, realizado en el repositorio git CPython; las pruebas con eval se usan mucho.fuente
Cuando
eval()
se usa para procesar la entrada proporcionada por el usuario, usted habilita al usuario para soltar a REPL proporcionando algo como esto:Puede salirse con la suya, pero normalmente no desea vectores para la ejecución de código arbitrario en sus aplicaciones.
fuente
Además de la respuesta de @Nadia Alramli, como soy nuevo en Python y estaba ansioso por comprobar cómo el uso
eval
afectará los tiempos , probé un pequeño programa y a continuación se muestran las observaciones:fuente