Today I ran again in the question why to use if-else when try-except is shorter?
There is a semantic part that we loose information on a try-except as we don’t know what the developer did expect in the most case and there is a performance part. I found a test for python 2 (Try / Except Performance in Python: A Simple Test) with simple assumptions: we try to get a value in a one keyed dictionary and make iterations to get a statistic on this access. I made it a little more complex with a dictionary with 10,001 keys.
Results analysis:
- if-else take the same amount of time in both case
- try-expect is 2 times faster than if-else when it no raise the exception
- try-expect is 5 times slower when the exception is raised
Then the if-else has a predictable comportment and help the maintainer.
I’m agree that the semantic part of the if-else can be replace by a comment but i still not recommend to try-except if it is not to raise an exception.
The results:
The case where the key does not exist: 1,000 iterations: with_try (0.250 ms) with_try_exc (0.291 ms) without_try (0.119 ms) without_try_not (0.120 ms) 1,000,000 iterations: with_try (231.647 ms) with_try_exc (263.633 ms) without_try (119.238 ms) without_try_not (118.602 ms) 1,000,000,000 iterations: with_try (224659.381 ms) with_try_exc (260333.897 ms) without_try (109796.531 ms) without_try_not (111871.690 ms) The case where the key does exist: 1,000 iterations: exists_with_try (0.066 ms) exists_with_try_exc (0.070 ms) exists_without_try (0.166 ms) exists_without_try_not (0.180 ms) 1,000,000 iterations: exists_with_try (57.661 ms) exists_with_try_exc (56.909 ms) exists_without_try (113.633 ms) exists_without_try_not (116.340 ms) 1,000,000,000 iterations: exists_with_try (57650.440 ms) exists_with_try_exc (57395.376 ms) exists_without_try (114659.023 ms) exists_without_try_not (117646.034 ms)
The code:
#!/usr/bin/env python3 import time d = dict.fromkeys(range(0, 10000), 0) d['somekey'] = 1 ITERATIONS = (1000, 1000*1000, 1000*1000*1000) def time_me(function): def wrap(*arg): start = time.time() r = function(*arg) end = time.time() print("%s (%0.3f ms)" % (function.__name__, (end-start)*1000)) return r return wrap # Not Existing @time_me def with_try(iterations): for i in range(0, iterations): try: get = d['notexist'] except: pass @time_me def with_try_exc(iterations): for i in range(0, iterations): try: get = d['notexist'] except Exception: pass @time_me def without_try(iterations): for i in range(0, iterations): if d.get('notexist'): pass else: pass @time_me def without_try_not(iterations): for i in range(0, iterations): if not d.get('notexist'): pass else: pass # Existing @time_me def exists_with_try(iterations): for i in range(0, iterations): try: get = d['somekey'] except: pass @time_me def exists_with_try_exc(iterations): for i in range(0, iterations): try: get = d['somekey'] except Exception: pass @time_me def exists_without_try(iterations): for i in range(0, iterations): if d.get('somekey'): pass else: pass @time_me def exists_without_try_not(iterations): for i in range(0, iterations): if not d.get('somekey'): pass else: pass print ("\n\nThe case where the key does not exist:") for iteration in ITERATIONS: print ("\n%d iterations:" % iteration) with_try(iteration) with_try_exc(iteration) without_try(iteration) without_try_not(iteration) print ("\n\nThe case where the key does exist:") for iteration in ITERATIONS: print ("\n%d iterations:" % iteration) exists_with_try(iteration) exists_with_try_exc(iteration) exists_without_try(iteration) exists_without_try_not(iteration)
