if hasattr(obj, 'attribute'):
# do somthing
vs
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
¿Cuál debería preferirse y por qué?
if hasattr(obj, 'attribute'):
# do somthing
vs
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
¿Cuál debería preferirse y por qué?
Respuestas:
hasattr
Realiza interna y rápidamente la misma tarea que eltry/except
bloque: es una herramienta muy específica, optimizada y de una sola tarea y, por lo tanto, debe preferirse, cuando corresponda, a la alternativa de propósito muy general.fuente
hasattr
va a capturar todas las excepciones en Python 2.x Vea mi respuesta para ver un ejemplo y la solución trivial.try
puede transmitir que la operación debería funcionar. Aunquetry
la intención no siempre es tal, es común, por lo que podría considerarse más legible.¿Algún banco que ilustre la diferencia en el rendimiento?
tiempo es tu amigo
$ python -mtimeit -s 'class C(object): a = 4 c = C()' 'hasattr(c, "nonexistent")' 1000000 loops, best of 3: 1.87 usec per loop $ python -mtimeit -s 'class C(object): a = 4 c = C()' 'hasattr(c, "a")' 1000000 loops, best of 3: 0.446 usec per loop $ python -mtimeit -s 'class C(object): a = 4 c = C()' 'try: c.a except: pass' 1000000 loops, best of 3: 0.247 usec per loop $ python -mtimeit -s 'class C(object): a = 4 c = C()' 'try: c.nonexistent except: pass' 100000 loops, best of 3: 3.13 usec per loop $ |positive|negative hasattr| 0.446 | 1.87 try | 0.247 | 3.13
fuente
try
es aproximadamente el doble de rápido quehasattr()
. Si no es así,try
es aproximadamente 1,5 veces más lento quehasattr()
(y ambos son sustancialmente más lentos que si el atributo existiera). Esto probablemente se deba a que, en el camino feliz,try
casi no hace nada (Python ya está pagando los gastos generales de las excepciones independientemente de si las usa), perohasattr()
requiere una búsqueda de nombre y una llamada a la función. En la ruta infeliz, ambos tienen que hacer un manejo de excepciones y agoto
, perohasattr()
lo hacen en C en lugar de código de bytes Python.Existe una tercera alternativa, que a menudo es mejor:
attr = getattr(obj, 'attribute', None) if attr is not None: print attr
Ventajas:
getattr
no tiene el mal comportamiento de deglución de excepciones señalado por Martin Geiser : en las antiguas Pythons,hasattr
incluso se traga unKeyboardInterrupt
.La razón normal por la que está comprobando si el objeto tiene un atributo es para poder usar el atributo, y esto naturalmente conduce a él.
El atributo se lee atómicamente y está a salvo de otros hilos que cambian el objeto. (Sin embargo, si esta es una preocupación importante, es posible que desee considerar bloquear el objeto antes de acceder a él).
Es más corto que
try/finally
y, a menudo, más corto quehasattr
.Un
except AttributeError
bloque amplio puede atrapar algo diferenteAttributeErrors
al que está esperando, lo que puede generar un comportamiento confuso.Acceder a un atributo es más lento que acceder a una variable local (especialmente si no es un atributo de instancia simple). (Aunque, para ser honesto, la microoptimización en Python es a menudo una tontería).
Una cosa con la que debe tener cuidado es que si le preocupa el caso en el que
obj.attribute
está configurado en Ninguno, deberá usar un valor centinela diferente.fuente
Casi siempre uso
hasattr
: es la opción correcta para la mayoría de los casos.El caso problemático es cuando una clase tiene prioridad
__getattr__
:hasattr
se capturar todas las excepciones en lugar de coger simplementeAttributeError
como usted espera. En otras palabras, el siguiente código se imprimiráb: False
aunque sería más apropiado ver unaValueError
excepción:class X(object): def __getattr__(self, attr): if attr == 'a': return 123 if attr == 'b': raise ValueError('important error from your database') raise AttributeError x = X() print 'a:', hasattr(x, 'a') print 'b:', hasattr(x, 'b') print 'c:', hasattr(x, 'c')
Así ha desaparecido el importante error. Esto se ha solucionado en Python 3.2 ( problema9666 ) donde
hasattr
ahora solo se detectaAttributeError
.Una solución fácil es escribir una función de utilidad como esta:
_notset = object() def safehasattr(thing, attr): return getattr(thing, attr, _notset) is not _notset
Esto nos
getattr
ocuparemos de la situación y luego podemos generar la excepción apropiada.fuente
hasattr
al menos no se atrape,KeyboardInterrupt
etc.safehasattr
, simplemente usegetattr
para copiar el valor en una variable local si lo va a usar, lo que casi siempre lo hace.hasattr
había mejorado así.hasattr
, y fui a comprobarlo. Tuvimos algunos errores bzr divertidos donde hasattr simplemente se tragó ^ C.Yo diría que depende de si su función puede aceptar objetos sin el atributo por diseño , por ejemplo, si tiene dos llamadores a la función, uno que proporciona un objeto con el atributo y el otro que proporciona un objeto sin él.
Si el único caso en el que obtendrá un objeto sin el atributo se debe a algún error, recomendaría usar el mecanismo de excepciones aunque sea más lento, porque creo que es un diseño más limpio.
En pocas palabras: creo que es un problema de diseño y legibilidad más que un problema de eficiencia.
fuente
Si no tener el atributo no es una condición de error, la variante de manejo de excepciones tiene un problema: detectaría también AttributeErrors que podrían aparecer internamente al acceder a obj.attribute (por ejemplo, porque el atributo es una propiedad, por lo que acceder a él llama a algún código).
fuente
Este tema fue tratado en la charla EuroPython 2016 Escribiendo Python más rápido por Sebastian Witowski. Aquí hay una reproducción de su diapositiva con el resumen de la actuación. También usa el aspecto de terminología antes de saltar en esta discusión, que vale la pena mencionar aquí para etiquetar esa palabra clave.
3 ¿PERMISOS O PERDÓN?
# CASE 1 -- Attribute Exists class Foo(object): hello = 'world' foo = Foo() if hasatter(foo, 'hello'): foo.hello ## 149ns ## try: foo.hello except AttributeError: pass ## 43.1 ns ## ## 3.5 times faster # CASE 2 -- Attribute Absent class Bar(object): pass bar = Bar() if hasattr(bar, 'hello'): bar.hello ## 428 ns ## try: bar.hello except AttributeError : pass ## 536 ns ## ## 25% slower
fuente
Si es solo un atributo lo que está probando, diría que use
hasattr
. Sin embargo, si está haciendo varios accesos a atributos que pueden o no existir, entonces usar untry
bloque puede ahorrarle algo de escritura.fuente
Sugeriría la opción 2. La opción 1 tiene una condición de carrera si algún otro hilo está agregando o quitando el atributo.
También pitón tiene un Idiom , que EAFP ( 'más fácil pedir perdón que pedir permiso') es mejor que LBYL ( 'mirar antes de saltar').
fuente
Desde un punto de vista práctico, en la mayoría de los lenguajes, usar un condicional siempre será considerablemente más rápido que manejar una excepción.
Si desea manejar el caso de un atributo que no existe en algún lugar fuera de la función actual, la excepción es la mejor manera de hacerlo. Un indicador de que es posible que desee usar una excepción en lugar de un condicional es que el condicional simplemente establece una bandera y aborta la operación actual, y algo en otra parte verifica esta bandera y toma medidas basadas en eso.
Dicho esto, como señala Rax Olgud, la comunicación con los demás es un atributo importante del código, y lo que quiere decir al decir "esta es una situación excepcional" en lugar de "esto es algo que espero que suceda" puede ser más importante .
fuente
El primero.
Cuanto más corto, mejor. Las excepciones deben ser excepcionales.
fuente
for
declaración y tambiénhasattr
usa una. Sin embargo, "más corto es mejor" (¡y "más simple es mejor"!) SÍ se aplica, por lo que es preferible el hasattr más simple, más corto y más específico.Al menos cuando se trata de lo que está sucediendo en el programa, omitiendo la parte humana de la legibilidad, etc. (que en realidad es la mayoría de las veces más importante que el rendimiento (al menos en este caso, con ese lapso de rendimiento), como señalaron Roee Adler y otros).
Sin embargo, mirándolo desde esa perspectiva, entonces se convierte en una cuestión de elegir entre
try: getattr(obj, attr) except: ...
y
try: obj.attr except: ...
ya que
hasattr
solo usa el primer caso para determinar el resultado. Comida para el pensamiento ;-)fuente