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

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

hasattr internally and rapidly performs the same task as the try/except block: it's a very specific, optimized, one-task tool and thus should be preferred, when applicable, to the very general-purpose alternative.


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.


I almost always use hasattr: it's the correct choice for most cases.

The problematic case is when a class overrides __getattr__: hasattr will catch all exceptions instead of catching just AttributeError like you expect. In other words, the code below will print b: False even though it would be more appropriate to see a ValueError exception:

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

The important error has thus disappeared. This has been fixed in Python 3.2 (issue9666) where hasattr now only catches AttributeError.

An easy workaround is to write a utility function like this:

_notset = object()

def safehasattr(thing, attr):
    return getattr(thing, attr, _notset) is not _notset

This let's getattr deal with the situation and it can then raise the appropriate exception.