Should I use "if" or "try" (as a matter of speed)?

Steven D'Aprano steve at REMOVETHIScyber.com.au
Sun Jul 10 04:48:51 CEST 2005


On Sat, 09 Jul 2005 23:10:49 +0200, Thomas Lotze wrote:

> Steve Juranich wrote:
> 
>> I was wondering how true this holds for Python, where exceptions are such
>> an integral part of the execution model.  It seems to me, that if I'm
>> executing a loop over a bunch of items, and I expect some condition to
>> hold for a majority of the cases, then a "try" block would be in order,
>> since I could eliminate a bunch of potentially costly comparisons for each
>> item.
> 
> Exactly.
> 
>> But in cases where I'm only trying a single getattr (for example),
>> using "if" might be a cheaper way to go.
> 
> Relying on exceptions is faster. In the Python world, this coding style
> is called EAFP (easier to ask forgiveness than permission). You can try
> it out, just do something 10**n times and measure the time it takes. Do
> this twice, once with prior checking and once relying on exceptions.

True, but only sometimes. It is easy to write a test that gives misleading
results.

In general, setting up a try...except block is cheap, but actually calling
the except clause is expensive. So in a test like this:

for i in range(10000):
    try:
        x = mydict["missing key"]
    except KeyError:
        print "Failed!"

will be very slow (especially if you time the print, which is slow).

On the other hand, this will be very fast:

for i in range(10000):
    try:
        x = mydict["existing key"]
    except KeyError:
        print "Failed!"

since the except is never called.

On the gripping hand, testing for errors before they happen will be slow
if errors are rare:

for i in range(10000):
    if i == 0:
        print "Failed!"
    else:
        x = 1.0/i

This only fails on the very first test, and never again.

When doing your test cases, try to avoid timing things unrelated to the
thing you are actually interested in, if you can help it. Especially I/O,
including print. Do lots of loops, if you can, so as to average away
random delays due to the operating system etc. But most importantly, your
test data must reflect the real data you expect. Are most tests
successful or unsuccessful? How do you know?

However, in general, there are two important points to consider.

- If your code has side effects (eg changing existing objects, writing to
files, etc), then you might want to test for error conditions first.
Otherwise, you can end up with your data in an inconsistent state.

Example:

L = [3, 5, 0, 2, 7, 9]

def invert(L):
    """Changes L in place by inverting each item."""
    try:
        for i in range(len(L)):
            L[i] = 1.0/L[i]
    except ZeroDivisionError:
        pass 

invert(L)
print L

=> [0.333, 0.2, 0, 2, 7, 9]


- Why are you optimizing your code now anyway? Get it working the simplest
way FIRST, then _time_ how long it runs. Then, if and only if it needs to
be faster, should you worry about optimizing. The simplest way will often
be try...except blocks.


-- 
Steven.




More information about the Python-list mailing list