2

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?

Kijewski
  • 25,517
  • 12
  • 101
  • 143
niladri chakrabarty
  • 503
  • 1
  • 7
  • 15
  • 11
    See [EAFP](https://docs.python.org/2/glossary.html#term-eafp) vs. [LBYL](https://docs.python.org/2/glossary.html#term-lbyl) – jonrsharpe Sep 14 '15 at 14:19
  • 1
    what about an example with `a/b+c/d+e/f+g/h`. Trivial examples don't necessarily teach good practice. – meuh Sep 14 '15 at 14:43

2 Answers2

2

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
Community
  • 1
  • 1
supervacuo
  • 9,072
  • 2
  • 44
  • 61
1

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.

Anand S Kumar
  • 88,551
  • 18
  • 188
  • 176
  • 2
    The numbers I found suggest that `try`/`except` is slightly faster than `if`, http://stackoverflow.com/a/3929887/399367 – supervacuo Sep 14 '15 at 14:27
  • 1
    (also, "alot" doesn't mean what you think it does…) – supervacuo Sep 14 '15 at 14:28
  • @supervacuo in your example case it only throws error once in the entire loop , even then the `try:except` block was just slightly more performant than the other one. I have included test results, you can see how more performant it would be to use `if` , if the number of 0s in the list being iterated over is even like 10% or so. – Anand S Kumar Sep 14 '15 at 14:35
  • 1
    I am not saying `try/except` is bad, its just that it basically depends on the case. Exceptions are for exceptional cases. – Anand S Kumar Sep 14 '15 at 14:37
  • 2
    @AnandSKumar within Python, I would disagree - EAFP is preferred in general over LBYL. This concrete numeric case though I would prefer LBYL too, because it IMHO produces code that expresses the algorithm better than catching exceptions. – deets Sep 14 '15 at 14:41
  • I am also talking about this case, where the OP says `y can be zero` . And I never said I prefer LBYL or EAFP , I said it depends on the case, if y being zero is really exceptional case, then EAFP is the way to go. If y being zero is common, then I would prefer `if` loops. – Anand S Kumar Sep 14 '15 at 14:43
  • As Anand says, Exceptions are for exceptional cases, so from the above numbers it makes sense to use `if` if you expect the divisor to be zero 5% or more of the time. Also see the timings in [this answer](http://stackoverflow.com/a/2522013/4014959). – PM 2Ring Sep 14 '15 at 14:49
  • 1
    Except the original question was about a one-off, not a tight loop where an exception might be raised on many iterations. Re @deets "concrete numeric case", it's not necessarily that clear-cut — see my answer. – supervacuo Sep 14 '15 at 15:21
  • *continue # or break, supposing there is a loop going on.* ? – Anand S Kumar Sep 14 '15 at 15:21
  • @supervacuo Again , I have no idea where you are getting the idea I am only suggesting `if` condition. I am saying it basically depends on how often , even if this is not run in a loop , but still how often y would be `0` . If its a common case that y would be zero, then using `try/except` in that case according to me is plain wrong, not just from the performance perspective, but also from what `Exceptions` stand for, they are to be used in an `Exceptional case` . If the developer already knows that y being zero is common, than that does not constitute an exceptional case. – Anand S Kumar Sep 14 '15 at 15:27