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:
with_try (1.562 ms)
with_try_exc (2.166 ms)
without_try (0.233 ms)
without_try_not (0.201 ms)
with_try (168.793 ms)
with_try_exc (223.589 ms)
without_try (24.877 ms)
without_try_not (20.992 ms)
with_try (1571.420 ms)
with_try_exc (2228.899 ms)
without_try (250.723 ms)
without_try_not (219.819 ms)
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)
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)
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)