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:
getattr
no tiene la mala excepcióngetattr
de deglución señalado por Martin Geiser- en el antiguo Pythons,hasattr
incluso se tragará unKeyboardInterrupt
.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.
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).
Es más corto que
try/finally
y a menudo más corto quehasattr
.Un bloque amplio
except AttributeError
puede atrapar otrosAttributeErrors
distintos a los que está esperando, lo que puede generar un comportamiento confuso.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