Python Duck Typing – Forgiveness vs Permission and Duck Typing

duck-typingexceptionspython

In Python, I often hear that it is better to "beg forgiveness" (exception catching) instead of "ask permission" (type/condition checking). In regards to enforcing duck typing in Python, is this

try:
    x = foo.bar
except AttributeError:
    pass
else:
    do(x)

better or worse than

if hasattr(foo, "bar"):
    do(foo.bar)
else:
    pass

in terms of performance, readability, "pythonic", or some other important factor?

Best Answer

It really depends on how often you think the exception is going to be thrown.

Both approaches are, in my opinion, equally valid, at least in terms of readability and pythonic-ness. But if 90% of your objects do not have the attribute bar you'll notice a distinct performance difference between the two approaches:

>>> import timeit
>>> def askforgiveness(foo=object()):
...     try:
...         x = foo.bar
...     except AttributeError:
...         pass
... 
>>> def askpermission(foo=object()):
...     if hasattr(foo, 'bar'):
...         x = foo.bar
... 
>>> timeit.timeit('testfunc()', 'from __main__ import askforgiveness as testfunc')
2.9459929466247559
>>> timeit.timeit('testfunc()', 'from __main__ import askpermission as testfunc')
1.0396890640258789

But if 90% of your objects do have the attribute, the tables have been turned:

>>> class Foo(object):
...     bar = None
... 
>>> foo = Foo()
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askforgiveness as testfunc, foo')
0.31336188316345215
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askpermission as testfunc, foo')
0.4864199161529541

So, from a performance point of view, you need to pick the approach that works best for your circumstances.

In the end, some strategic use of the timeit module may be the most Pythonic thing you can do.