has error attribute python exception exception-handling attributes hasattr

attribute error python object has no attribute



hasattr() vs try-except block para tratar con atributos inexistentes (12)

if hasattr(obj, ''attribute''): # do somthing

vs

try: # access obj.attribute except AttributeError, e: # deal with AttributeError

¿Cuál debería ser el preferido y por qué?


Al menos cuando depende de lo que ocurre en el programa, dejando de lado la parte humana de la legibilidad, etc. (que en realidad es la mayor parte del tiempo más importante que el rendimiento (al menos en este caso, con ese intervalo de rendimiento), como Roee Adler y otros señalaron).

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


Casi siempre uso hasattr : es la elección correcta para la mayoría de los casos.

El caso problemático es cuando una clase anula __getattr__ : hasattr todas las excepciones en lugar de detectar solo AttributeError como esperabas. En otras palabras, el código siguiente imprimirá b: False , aunque sería más apropiado ver una excepción ValueError :

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

El error importante ha desaparecido. Esto se ha corregido en Python 3.2 ( issue9666 ) donde hasattr ahora solo captura AttributeError .

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 vamos a tratar getattr la situación y luego puede plantear la excepción apropiada.


Desde un punto de vista práctico, en la mayoría de los lenguajes que usan un condicional siempre será mucho 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 puede querer usar una excepción en lugar de un condicional es que el condicional simplemente establece un indicador y anula la operación actual, y algo en otro lugar verifica este indicador y toma medidas en función de 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 pase" puede ser más importante. .


El primero.

Más corto es mejor. Las excepciones deben ser excepcionales.


Este tema fue cubierto en la charla de EuroPython 2016 Writing Python más rápido de Sebastian Witowski. Aquí hay una reproducción de su diapositiva con el resumen de rendimiento. También utiliza la terminología antes de saltar en esta discusión, vale la pena mencionar aquí para etiquetar esa palabra clave.

Si el atributo realmente falta, pedir limosna será más lento que pedir permisos. De modo que, como regla general, puede usar el método de pedir permiso si sabe que es muy probable que falte el atributo u otros problemas que pueda predecir. De lo contrario, si espera que el código resultará en la mayoría de las veces código legible

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


Hay una tercera alternativa, a menudo mejor:

attr = getattr(obj, ''attribute'', None) if attr is not None: print attr

Ventajas:

  1. getattr no tiene la mala excepción getattr de deglución señalado por Martin Geiser- en el antiguo Pythons, hasattr incluso se tragará un KeyboardInterrupt .

  2. La razón normal por la que está comprobando si el objeto tiene un atributo es para que pueda usar el atributo, y esto naturalmente lo lleva a él.

  3. El atributo se lee atómicamente y está a salvo de otros hilos que cambian el objeto. (Sin embargo, si esto es una preocupación importante, es posible que desee considerar bloquear el objeto antes de acceder a él).

  4. Es más corto que try/finally y a menudo más corto que hasattr .

  5. Un bloque amplio except AttributeError puede atrapar otros AttributeErrors distintos a los que está esperando, lo que puede generar un comportamiento confuso.

  6. El acceso a un atributo es más lento que el acceso a una variable local (especialmente si no es un atributo de instancia simple). (Sin embargo, para ser honesto, la micro-optimización en Python es a menudo un mandado tonto).

Una cosa a tener en cuenta es que si le importa el caso en el que obj.attribute está establecido en None, necesitará usar un valor centinela diferente.


Si no tener el atributo no es una condición de error, la variante de manejo de excepciones tiene un problema: atrapará también AttributeErrors que podría venir internamente al acceder a obj.attribute (por ejemplo, porque el atributo es una propiedad para que acceder a él acceda a algún código).


Si solo está probando un atributo, yo diría que use hasattr . Sin embargo, si está haciendo varios accesos a atributos que pueden existir o no, entonces usar un bloque try puede ahorrarle algo de tipeo.


Sugeriría la opción 2. La opción 1 tiene una condición de carrera si algún otro hilo está agregando o eliminando el atributo.

También Python tiene un idioma, ese EAFP (''más fácil pedir perdón que permiso'') es mejor que LBYL (''mira antes de saltar'').


Yo diría que depende de si su función puede aceptar objetos sin el atributo por diseño , por ejemplo, si tiene dos llamantes a la función, uno proporciona un objeto con el atributo y el otro proporciona un objeto sin él.

Si el único caso en el que obtendrá un objeto sin el atributo se debe a algún error, le 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.


hasattr interna y rápidamente la misma tarea que el bloque try/except : es una herramienta muy específica, optimizada y de una sola tarea, por lo que debe preferirse, cuando corresponda, a la alternativa de propósito general.


Cualquier banco que ilustra la diferencia en el rendimiento?

Timeit 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