Try / Except Performance in Python: A Simple Test

I was discussing the wisdom of using try/except throughout Python code today with someone and there were a couple of points that I felt would be quick and easy to verify or debunk.


I wrote a quick little script to time a a set of functions testing the use cases of not knowing whether an element in a dictionary exists or not prior to referencing it. There are a set where it doesn’t exist and set where it does. The numbers are interesting:

The case where the key does not exist

1,000 iterations

with_try (1.562 ms)
with_try_exc (2.166 ms)
without_try (0.233 ms)
without_try_not (0.201 ms)

100,000 iterations

with_try (168.793 ms)
with_try_exc (223.589 ms)
without_try (24.877 ms)
without_try_not (20.992 ms)

1,000,000 iterations

with_try (1571.420 ms)
with_try_exc (2228.899 ms)
without_try (250.723 ms)
without_try_not (219.819 ms)

The case where the key does exist

1,000 iterations

exists_with_try (0.154 ms)
exists_with_try_exc (0.141 ms)
exists_without_try (0.216 ms)
exists_without_try_not (0.220 ms)

100,000 iterations

exists_with_try (15.647 ms)
exists_with_try_exc (15.165 ms)
exists_without_try (22.302 ms)
exists_without_try_not (23.364 ms)

1,000,000 iterations

exists_with_try (158.330 ms)
exists_with_try_exc (158.038 ms)
exists_without_try (233.005 ms)
exists_without_try_not (237.813 ms)

From these results, I think it is fair to quickly determine a number of conclusions:

1. If there is a high likelihood that the element doesn’t exist, then you are better off checking for it with has_key. 2. If you are not going to do anything with the Exception if it is raised, then you are better off not putting one have the except 3. If it is likely that the element does exist, then there is a very slight advantage to using a try/except block instead of using has_key, however, the advantage is very slight.

There is obviously a lot missing from this analysis and it really doesn’t provide anything of earth-shattering revelation — using exception handling logic where you don’t expect to encounter many exceptions is going to be more expensive. It just seemed like a good excuse to write a little Python. :)

For those interested here is the script I wrote to arrive at the numbers above:

import time

def time_me(function):
    def wrap(*arg):
        start = time.time()
        r = function(*arg)
        end = time.time()
        print "%s (%0.3f ms)" % (function.func_name, (end-start)*1000)
        return r
    return wrap
    

# Not Existing
@time_me
def with_try(iterations):
    d = {'somekey': 123}
    for i in range(0, iterations):
        try:
            get = d['notexist']
        except:
            pass

@time_me
def with_try_exc(iterations):
    d = {'somekey': 123}
    for i in range(0, iterations):
        try:
            get = d['notexist']
        except Exception, e:
            pass
            
@time_me
def without_try(iterations):
    d = {'somekey': 123}
    for i in range(0, iterations):
        if d.has_key('notexist'):
            pass
        else:
            pass
            
@time_me
def without_try_not(iterations):
    d = {'somekey': 123}
    for i in range(0, iterations):
        if not d.has_key('notexist'):
            pass
        else:
            pass
            
    
    
# Existing
@time_me
def exists_with_try(iterations):
    d = {'somekey': 123}
    for i in range(0, iterations):
        try:
            get = d['somekey']
        except:
            pass

@time_me
def exists_with_try_exc(iterations):
    d = {'somekey': 123}
    for i in range(0, iterations):
        try:
            get = d['somekey']
        except Exception, e:
            pass
            
@time_me
def exists_without_try(iterations):
    d = {'somekey': 123}
    for i in range(0, iterations):
        if d.has_key('somekey'):
            pass
        else:
            pass
            
@time_me
def exists_without_try_not(iterations):
    d = {'somekey': 123}
    for i in range(0, iterations):
        if not d.has_key('somekey'):
            pass
        else:
            pass
        
        
print "The case where the key does not exist:"
print "1,000 iterations:"
with_try(1000)
with_try_exc(1000)
without_try(1000)
without_try_not(1000)

print "\n100,000 iterations:"
with_try(100000)
with_try_exc(100000)
without_try(100000)
without_try_not(100000)

print "\n1,000,000 iterations:"
with_try(1000000)
with_try_exc(1000000)
without_try(1000000)
without_try_not(1000000)

print "\n\nThe case where the key does exist:"
print "1,000 iterations:"
exists_with_try(1000)
exists_with_try_exc(1000)
exists_without_try(1000)
exists_without_try_not(1000)

print "\n100,000 iterations:"
exists_with_try(100000)
exists_with_try_exc(100000)
exists_without_try(100000)
exists_without_try_not(100000)

print "\n1,000,000 iterations:"
exists_with_try(1000000)
exists_with_try_exc(1000000)
exists_without_try(1000000)
exists_without_try_not(1000000)