if hasattr(obj, 'attribute'):
# do somthing
contre
try:
# access obj.attribute
except AttributeError, e:
# deal with AttributeError
Lequel devrait être préféré et pourquoi?
hasattr
effectue en interne et rapidement la même tâche que le bloc try/except
: c’est un outil très spécifique, optimisé, à tâche unique et doit donc être préféré, le cas échéant, à l’alternative très générale.
Des bancs qui illustrent la différence de performance?
c'est ton ami
$ 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
Il existe une troisième alternative, souvent meilleure:
attr = getattr(obj, 'attribute', None)
if attr is not None:
print attr
Avantages:
getattr
n'a pas le mauvais comportement d'absorption des exceptions signalé par Martin Geiser - dans les anciens Pythons, hasattr
avalera même une KeyboardInterrupt
.
La raison normale pour laquelle vous vérifiez si l'objet a un attribut est que vous pouvez utiliser cet attribut, ce qui le conduit naturellement.
L'attribut est lu de manière atomique et est protégé des autres threads modifiant l'objet. (Cependant, s'il s'agit d'une préoccupation majeure, vous pouvez envisager de verrouiller l'objet avant d'y accéder.)
Il est plus court que try/finally
et souvent plus court que hasattr
.
Un bloc except AttributeError
large peut intercepter une AttributeErrors
différente de celle à laquelle vous vous attendez, ce qui peut entraîner un comportement déroutant.
L'accès à un attribut est plus lent que l'accès à une variable locale (surtout s'il ne s'agit pas d'un attribut d'instance simple). (Bien que, pour être honnête, la micro-optimisation en Python est souvent une course dupe.)
Si vous vous souciez du cas où obj.attribute
est défini sur None, vous devez utiliser une valeur sentinel différente.
J'utilise presque toujours hasattr
: c'est le bon choix pour la plupart des cas.
Le cas problématique est le cas où une classe remplace __getattr__
: hasattr
va intercepter toutes les exceptions au lieu d’attraper simplement AttributeError
comme prévu. En d'autres termes, le code ci-dessous imprimera b: False
même s'il serait plus approprié de voir une exception 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')
L'erreur importante a donc disparu. Cela a été corrigé dans Python 3.2 _ ( issue9666 ) où hasattr
ne capture plus que AttributeError
.
Une solution de contournement facile consiste à écrire une fonction utilitaire comme celle-ci:
_notset = object()
def safehasattr(thing, attr):
return getattr(thing, attr, _notset) is not _notset
getattr
traite de la situation et peut alors lever l'exception appropriée.
Je dirais que cela dépend si votre fonction peut accepter des objets sans l'attribut de par sa conception , par exemple. si vous avez deux appelants à la fonction, l'un fournissant un objet avec l'attribut et l'autre fournissant un objet sans cet attribut.
Si le seul cas où vous obtiendrez un objet sans l'attribut est dû à une erreur, je vous recommanderais d'utiliser le mécanisme des exceptions même s'il peut être plus lent, car je pense que la conception est plus claire.
En bout de ligne: Je pense que c'est un problème de conception et de lisibilité plutôt qu'un problème d'efficacité.
Si l'attribut n'est pas non une condition d'erreur, la variante de gestion des exceptions a un problème: elle intercepterait également AttributeErrors qui pourrait venir internally lors de l'accès à obj.attribute (par exemple, attribut étant un propriété afin qu’y accéder appelle du code).
Si vous testez un attribut, je dirais que vous utilisez hasattr
. Cependant, si vous utilisez plusieurs accès à des attributs qui peuvent ou non exister, alors un bloc try
peut vous faire économiser du frappe.
Je suggérerais l'option 2. L'option 1 a une condition de concurrence critique si un autre thread ajoute ou supprime l'attribut.
Python a aussi un Idiom , que EAFP ("plus facile de demander pardon que la permission") est meilleur que LBYL ("regarde avant de sauter").
D'un point de vue pratique, dans la plupart des langues, l'utilisation d'un conditionnel sera toujours beaucoup plus rapide que le traitement d'une exception.
Si vous souhaitez gérer le cas d'un attribut qui n'existe pas quelque part en dehors de la fonction actuelle, l'exception est la meilleure solution. Un indicateur indiquant que vous souhaiterez peut-être utiliser une exception au lieu d'une condition est que la condition conditionnelle se contente de définir un indicateur et d'abandonner l'opération en cours. Un contrôle vérifie ailleurs cet indicateur et prend des mesures en conséquence.
Cela dit, comme le souligne Rax Olgud, la communication avec les autres est un attribut important du code, et ce que vous voulez dire en disant "c'est une situation exceptionnelle" plutôt que "c'est quelque chose que je prévois d'arriver" peut être plus important .
Du moins quand il s'agit de savoir exactement ce qui se passe dans le programme, en laissant de côté la partie humaine de la lisibilité, etc. comme Roee Adler et d’autres l’ont fait remarquer).
Néanmoins, sous cet angle, .__, il faut alors choisir entre:
try: getattr(obj, attr)
except: ...
et
try: obj.attr
except: ...
puisque hasattr
utilise simplement le premier cas pour déterminer le résultat . Matière à réflexion ;-)
Ce sujet a été traité dans le discours sur EuroPython 2016 Ecrire plus vite Python de Sebastian Witowski. Voici une reproduction de sa diapositive avec le résumé de la performance. Il utilise également la terminologie regardez avant de sauter dans cette discussion, il convient de le mentionner ici pour baliser ce mot clé.
Si l'attribut est réellement manquant, alors quémander du pardon le fera être plus lent que de demander des autorisations. Donc, en règle générale, vous pouvez utilisez la méthode de demande de permission si vous savez qu'il est très probable que le attribut sera manquant ou d'autres problèmes que vous pouvez prédire . Sinon, si vous vous attendez à ce que le code soit lisible, la plupart du temps code
# 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
La première.
Plus c'est court, mieux c'est. Les exceptions devraient être exceptionnelles.