Últimamente he estado leyendo algunos tweets y la documentación de python sobre hasattr y dice:
hasattr (objeto, nombre)
- ¿Cómo consultar una búsqueda avanzada con la API de google customsearch?
- Ámbito de comprensión de lista anidada
- Asyncio web scraping 101: recuperando múltiples urls con aiohttp
- Mostrar solo el puntaje más alto de las 3 puntuaciones más recientes de una persona, guardado en un archivo .txt
- Llenar diccionario de la lista
Los argumentos son un objeto y una cadena. El resultado es Verdadero si la cadena es el nombre de >> uno de los atributos del objeto, Falso si no lo es. (Esto se implementa llamando a getattr (objeto, nombre) y viendo si genera un AttributeError o no).
Hay un lema en Python que dice que es más fácil pedir perdón que un permiso donde generalmente estoy de acuerdo.
Intenté hacer una prueba de rendimiento en este caso con un código de Python realmente simple:
import timeit definition="""\ class A(object): a = 1 a = A() """ stm="""\ hasattr(a, 'a') """ print timeit.timeit(stmt=stm, setup=definition, number=10000000) stm="""\ getattr(a, 'a') """ print timeit.timeit(stmt=stm, setup=definition, number=10000000)
Con los resultados:
$ python test.py hasattr(a, 'a') 1.26515984535 getattr(a, 'a') 1.32518696785
También he intentado lo que sucede si el atributo no existe y las diferencias entre getattr y hasattr son mayores. Entonces, lo que he visto hasta ahora es que getattr es más lento que hasattr, pero en la documentación dice que llama a getattr.
He buscado la implementación CPython de hasattr y getattr y parece que ambos llaman a la siguiente función:
v = PyObject_GetAttr(v, name);
pero hay más repetitivo en getattr que en hasattr que probablemente lo hace más lento.
¿Alguien sabe por qué en la documentación decimos que hasattr llama a getattr y parece que animamos a los usuarios a usar getattr en lugar de hasattr cuando en realidad no se debe al rendimiento? ¿Es solo porque es más python?
Tal vez estoy haciendo algo mal en mi prueba 🙂
Gracias,
Raúl
La documentación no alienta, la documentación simplemente establece lo obvio. El hasattr
se implementa como tal, y lanzar un AttributeError
desde un generador de propiedades puede hacer que parezca que el atributo no existe. Este es un detalle importante, y es por eso que se establece explícitamente en la documentación. Considere por ejemplo este código:
class Spam(object): sausages = False @property def eggs(self): if self.sausages: return 42 raise AttributeError("No eggs without sausages") @property def invalid(self): return self.foobar spam = Spam() print(hasattr(Spam, 'eggs')) print(hasattr(spam, 'eggs')) spam.sausages = True print(hasattr(spam, 'eggs')) print(hasattr(spam, 'invalid'))
El resultado es
True False True False
Es decir, la clase Spam
tiene un descriptor de propiedad para los eggs
, pero como el getter genera AttributeError
si not self.sausages
, entonces la instancia de esa clase no tiene eggs
” hasattr
“.
Aparte de eso, use hasattr
solo cuando no necesite el valor ; si necesita el valor, use getattr
con 2 argumentos y capture la excepción, o 3 argumentos, el tercero es un valor predeterminado razonable.
Los resultados utilizando getattr()
(2.7.9):
>>> spam = Spam() >>> print(getattr(Spam, 'eggs')) >>> print(getattr(spam, 'eggs')) Traceback (most recent call last): File "", line 1, in File "", line 7, in eggs AttributeError: No eggs without sausages >>> spam.sausages = True >>> print(getattr(spam, 'eggs')) 42 >>> print(getattr(spam, 'invalid')) Traceback (most recent call last): File " ", line 1, in File "", line 10, in invalid AttributeError: 'Spam' object has no attribute 'invalid' >>>
Parece que hasattr
tiene un problema con las excepciones de deglución (al menos en Python 2.7 ), por lo que probablemente es mejor mantenerse alejado de él hasta que se solucione.
Tomemos, por ejemplo, el siguiente código :
>>> class Foo(object): ... @property ... def my_attr(self): ... raise ValueError('nope, nope, nope') ... >>> bar = Foo() >>> bar.my_attr Traceback (most recent call last): File "", line 1, in File "", line 4, in my_attr ValueError: nope, nope, nope >>> hasattr(Foo, 'my_attr') True >>> hasattr(bar, 'my_attr') False >>> getattr(bar, 'my_attr', None) Traceback (most recent call last): File " ", line 1, in File "", line 4, in my_attr ValueError: nope, nope, nope >>>