This question already has an answer here:
- Using try vs if in python 9 answers
When is exception handling more preferable than condition checking? There are many situations where I can choose using one or the other.
For example, this is a summing function which uses a custom exception:
# module mylibrary
class WrongSummand(Exception):
pass
def sum_(a, b):
""" returns the sum of two summands of the same type """
if type(a) != type(b):
raise WrongSummand("given arguments are not of the same type")
return a + b
# module application using mylibrary
from mylibrary import sum_, WrongSummand
try:
print sum_("A", 5)
except WrongSummand:
print "wrong arguments"
And this is the same function, which avoids using exceptions
# module mylibrary
def sum_(a, b):
""" returns the sum of two summands if they are both of the same type """
if type(a) == type(b):
return a + b
# module application using mylibrary
from mylibrary import sum_
c = sum_("A", 5)
if c is not None:
print c
else:
print "wrong arguments"
I think that using conditions is always more readable and manageable. Or am I wrong? What are the proper cases for defining APIs which raise exceptions and why?
Exceptions are much more manageable, because they define general families of things that can go wrong. In your example there is only one possible problem, so there is no advantage to using exceptions. But if you had another class that does division, then it needs to signal that you can't devide by zero. Simply returning
None
wouldn't work anymore.On the other hand, exceptions can be subclassed and you can catch specific exceptions, depending on how much you care about the underlying problem. For example, you could have a
DoesntCompute
base exception and subclasses likeInvalidType
andInvalidArgument
. If you just want a result, you can wrap all computations in a block that catchesDoesntCompute
, but you can still do very specific error handling just as easy.Generally, you want to use condition checking for situations which are understandable, expected, and able to be handled. You would use exceptions for cases that are incoherent or unhandleable.
So, if you think of your "add" function. It should NEVER return null. That is not a coherent result for adding two things. In that case, there is an error in the arguments that were passed in and the function should not attempt to pretend that everything is okay. This is a perfect case to throw an exception.
You would want to use condition checking and return null if you are in a regular or normal execution case. For instance,
IsEqual
could be a good case to use conditions, and return false if one of your conditions fails. I.E.In that scenario, you are returning false both for the exception cases AND the "objects are not equal case". This means that the consumer (calling party) cannot tell whether the comparison failed or the objects were simply not equal. If those cases need to be distinguished, then you should use exceptions instead of conditions.
Ultimately, you want to ask yourself whether the consumer will be able to specifically handle the failure case that you encountered. If your method/function cannot do what it needs to do then you probably want to throw an exception.
Maybe
sum_
looks fine alone. What if, you know, it actually is used?If you ran
bar.py
you would get:See -- usually one calls a function with the intent to act on its output. If you simply "swallow" the exception and return a dummy value, who uses your code will have an hard time troubleshooting. First off, the traceback is completely useless. This alone should be enough reason.
Who wants to fix this bug would have to first doublecheck
bar.py
, then analizeegg.py
trying to figure out where exactly the None came from. After readingegg.py
they'll have to readsum_.py
and hopefully notice the implicit return ofNone
; only then they understand the problem: they failed the type check because of the parameteregg.py
put in for them.Put a bit of actual complexity in this and thing get ugly really fast.
Python, unlike C, is written with the Easier to Ask Forgiveness than Permission principle in mind: if something goes wrong, I'll get an exception. If you pass me a
None
where I expect an actual value, things will break, the exception will happen far away from the line actually causing it and people will curse in your general direction in twenty different languages, then change the code to throw a suitable exception (TypeError("incompatible operand type")
).Actually, the problem of using exceptions lies in the business logic. If the situation is exception (i.e. should not happen at all), an exception can be used. However, if the situation is possible from the business logic point of view, when it should be handled by conditional checking, even if this condition looks much more complicated.
For example, here is the code that I met in the prepared statement, when the developer is setting parameter values (Java, not Python):
With conditional checking this would be written like this:
Variant B seems much more complicated from the first sight, however, it is the correct one, since this situation is possible from the business point of view (the country may not be specified). Using exceptions will cause performance issues, and will lead to misunderstanding of code, since it will not be clear, is it acceptable for the country to be empty.
Variant B can be improved by using auxiliary functions in EnterpriseBean that will return the region and country immediately:
This code uses something like chaining, and each get method seems simple enough and is using only one predecessor. Therefore variant B can be rewritten as follows:
Also, read this Joel article about why exceptions should not be overused. And an essay by Raymon Chen.
You should throw exception when the parameter contains an unexpected value.
With your examples, I would recommend to throw exception when the two parameters are of different types.
To throw an exception is an elegant way to abort a service without cluttering up your code.
My main reason for preferring exceptions to status returns has to do with considering what happens if the programmer forgets to do his job. With exceptions, you might overlook catching an exception. In that case, your system will visibly fail, and you'll have a chance to consider where to add a catch. With status returns, if you forget to check the return, it will be silently ignore, and your code will continue on, possibly failing later in a mysterious way. I prefer the visible failure to the invisible one.
There are other reasons, which I've explained here: Exceptions vs. Status Returns.