hasattr() vs try-except block to deal with non-exi

2019-01-10 09:27发布

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

vs

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

Which should be preferred and why?

12条回答
\"骚年 ilove
2楼-- · 2019-01-10 09:54

If not having the attribute is not an error condition, the exception handling variant has a problem: it would catch also AttributeErrors that might come internally when accessing obj.attribute (for instance because attribute is a property so that accessing it calls some code).

查看更多
迷人小祖宗
3楼-- · 2019-01-10 09:56

If it's just one attribute you're testing, I'd say use hasattr. However, if you're doing several accesses to attributes which may or may not exist then using a try block may save you some typing.

查看更多
家丑人穷心不美
4楼-- · 2019-01-10 09:56

This subject was covered in the EuroPython 2016 talk Writing faster Python by Sebastian Witowski. Here's a reproduction of his slide with the performance summary. He also uses the terminology look before you leap in this discussion, worth mentioning here to tag that keyword.

If the attribute is actually missing then begging for forgiveness will be slower than asking for permissions. So as a rule of thumb you can use the ask for permission way if know that it is very likely that the attribute will be missing or other problems that you can predict. Otherwise if you expect code will result in most of the times readable code

3 PERMISSIONS OR FORGIVENESS?

# 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
查看更多
来,给爷笑一个
5楼-- · 2019-01-10 09:58

At least when it is up to just what's going on in the program, leaving out the human part of readability, etc. (which is actually most of the time more imortant than performance (at least in this case - with that performance span), as Roee Adler and others pointed out).

Nevertheless looking at it from that perspective, it then becomes a matter of choosing between

try: getattr(obj, attr)
except: ...

and

try: obj.attr
except: ...

since hasattr just uses the first case to determine the result. Food for thought ;-)

查看更多
等我变得足够好
6楼-- · 2019-01-10 10:01

Any benches that illustrate difference in performance?

timeit it's your friend

$ 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
查看更多
男人必须洒脱
7楼-- · 2019-01-10 10:02

There is a third, and often better, alternative:

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

Advantages:

  1. getattr does not have the bad exception-swallowing behavior pointed out by Martin Geiser - in old Pythons, hasattr will even swallow a KeyboardInterrupt.

  2. The normal reason you're checking if the object has an attribute is so that you can use the attribute, and this naturally leads in to it.

  3. The attribute is read off atomically, and is safe from other threads changing the object. (Though, if this is a major concern you might want to consider locking the object before accessing it.)

  4. It's shorter than try/finally and often shorter than hasattr.

  5. A broad except AttributeError block can catch other AttributeErrors than the one you're expecting, which can lead to confusing behaviour.

  6. Accessing an attribute is slower than accessing a local variable (especially if it's not a plain instance attribute). (Though, to be honest, micro-optimization in Python is often a fool's errand.)

One thing to be careful of is if you care about the case where obj.attribute is set to None, you'll need to use a different sentinel value.

查看更多
登录 后发表回答