Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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?

share|improve this question
12  
there is a third option, don't do anything and treat any foo without a bar as a bug –  jk. Nov 13 '12 at 9:37
    
I remember hearing that hasattr is implemented with that exact try/catch internally. Not certain if it's true... (it would act differently on properties, wouldn't it? Maybe I'm thinking of getattr..) –  Izkata Nov 13 '12 at 14:09
    
@Izkata: The implementation of hasattr does use the C-API equivalent of getattr (return True if successful, False if not), but handling exceptions in C is a lot faster. –  Martijn Pieters Nov 13 '12 at 16:26
2  
I've accepted martijn's answer, but I'd like to add that if you're trying to set an attribute, you should definitely consider using try/catch because it may be a property without a setter, in which case hasattr will be true, but it will still raise AttributeError. –  darkfeline Nov 18 '12 at 20:58

3 Answers 3

up vote 42 down vote accepted

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.

share|improve this answer
    
Some weeks ago I asked this question: programmers.stackexchange.com/questions/161798/… There I asked whether in loosely typed languages you had to work extra doing type checks, and I was bombarded by people saying you hadn't. Know I see you have. –  user61852 Nov 20 '12 at 15:26
    
@user1598390: When you define an API that expects a homogenous mix of types, you have to do some tests. Most of the time, you don't. This is specific area from which you cannot derive rules about paradigms as a whole, I'm afraid. –  Martijn Pieters Nov 20 '12 at 16:10
    
Well, any serious system development involves defining an API. So I guess type strict languages are best for that because you have to code less since the compiler checks the types for you in compile time. –  user61852 Nov 20 '12 at 18:06
    
It seems a bit inelegant to write timeit.timeit('testfunc()', 'from __main__ import askforgiveness as testfunc') instead of timeit.timeit(askforgiveness) –  Gareth Rees Apr 9 '13 at 15:59
    
@GarethRees: it establishes a pattern for the latter half of the answer where I pass in an argument to the function-under-test. –  Martijn Pieters Apr 9 '13 at 16:13

In python you often get better performance doing things the Python way. With other languages, using exceptions for flow-control is generally regarded as a terrible idea because exceptions typically impose an extraordinary overhead. But because this technique is explicitly endorsed in Python, the interpreter is optimized for this type of code.

As with all performance questions, the only way to be certain is to profile your code. Write both versions and see which one runs faster. Though in my experience, the "Python way" is typically the fastest way.

share|improve this answer

Performance, I feel, is a secondary concern. If it arises, a profiler would help you focus on the real bottlenecks, which may or may not be how you treat possible illegal arguments.

Readability and simplicity, on the other hand, are always a prime concern. There are no hard rules here, just use your judgment.

This is a universal issue, but environment- or language-specific conventions are relevant. For example, in Python it's usually fine to simply use the attribute you expect and let a possible AttributeError reach the caller.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.