Reputation: 503
Suppose I have two variables x & y, which can be any values. I have to divide x by y. y can be zero. Now I have two approaches
1)
if y == 0:
continue # or break, supposing there is a loop going on.
2)
try:
print 1/0
except ZeroDivisionError as e:
print e
finally:
print "got"
My question is, does approach 2 has any benefit over approach 1, if it is, then what is it?
Upvotes: 2
Views: 319
Reputation: 9242
try
/except
might be slightly faster.
In general, the "Pythonic way" is to try to do the thing you want to do and handle an error if it doesn't work ("Easier to Ask Forgiveness than Permission"), rather than pre-checking ("Look Before You Leap"), to give more flexibility in future.
It seems like y
is usually an integer (instance of int
) in your code.. but imagine that your requirements evolve, and y
is an instance of NewClass
and can now hold a value like -0.0001
.
In order to maintain compatibility with your application, NewClass.__eq__()
is written so that if y == 0
will do an integer comparison (i.e. would be True
in this instance), but NewClass.__truediv__
uses the float value, so 1 / y
won't return an error.
So, if you use if
your application will act like -0.0001 == 0
whereas using except ZeroDivisionError
will handle the new behaviour correctly.
class NewClass(int):
def __eq__(self, other):
return int(self) == int(other)
def __truediv__(self, other):
return self.value / other
y = NewClass(-0.0001)
if y != 0:
print 1 / y # not printed
try:
print 1 / y # printed
except ZeroDivisionError:
pass
Upvotes: 2
Reputation: 90889
From a performance perspective I would say whether option 2 has any benefit over option 1 depends on how often y
is 0.
If y
being 0 is common, then option 2 would be less preferable as it would end up raising the ZeroDivisionError very often , whereas doing the if y == 0:
or if y != 0:
check would be less time consuming.
But if y is rarely 0 (that is its an exception case), then option 2 would be more preferable as you would not have the overhead of doing the if
check and only very rarely would an error be raised.
Example of timing -
In [4]: def func2(lst):
...: for i in lst:
...: try:
...: x = 10/i
...: except ZeroDivisionError:
...: pass
...:
In [5]:
In [5]: def func1(lst):
...: for i in lst:
...: if i != 0:
...: x = 10/i
...:
In [6]: lst1 = [0 for i in range(100)]
In [7]: import random
In [8]: lst2 = [random.randrange(0,5) for i in range(100)]
In [9]: lst3 = [random.randrange(0,15) for i in range(100)]
In [10]: lst2.count(0)
Out[10]: 24
In [11]: lst3.count(0)
Out[11]: 5
In [12]: %timeit func1(lst1)
100000 loops, best of 3: 3.56 µs per loop
In [13]: %timeit func2(lst1)
10000 loops, best of 3: 50.6 µs per loop
In [14]: %timeit func1(lst2)
100000 loops, best of 3: 7.73 µs per loop
In [15]: %timeit func2(lst2)
10000 loops, best of 3: 18 µs per loop
In [16]: %timeit func1(lst3)
100000 loops, best of 3: 8.76 µs per loop
In [17]: %timeit func2(lst3)
100000 loops, best of 3: 9.88 µs per loop
Even with only 5% of the numbers being 0 , the if
loop was still more performant than the try/except
block.
Basically, Exceptions should be for Exceptional cases.
Upvotes: 1