Dealing with Errors
Is it a bad practice? To my opinion: No, it's not. In general this is GOOD practice:
def foo(a, b):
return a / b
def bar(a):
return foo(a, 0)
try:
bar(6)
except ZeroDivisionError:
print("Error!")
The reason is simple: Code dealing with the error is concentrated at a single point in your main program.
In some programming languages exceptions that could potentially be raised must be declared on function/method level. Python is different: It is a script language that lacks features like this. Of course therefore you might get an exception quite unexpectedly at some times as you might not be aware that other code you're invoking could raise such an exception. But that is no big deal: To resolve that situation you have the try...except...
in your main program.
You can compensate for this lack of knowledge about possible exceptions as follows:
- document exceptions that could be raised; if the programming language does not help here by itself, you need to make up for this deficit by providing a more extensive documentation;
- perform extensive tests;
In general it makes no sense at all to follow your option b). Things might be more explicit but the code itself is not the right place for this explicit information. Instead this information should be part of your function's/method's documentation.
Therefore instead of ...
def bar(a):
try:
ret = foo(a, 0)
except ZeroDivisionError:
raise
return ret
... write:
def bar(a):
"""
Might raise ZeroDivisionError
"""
return foo(a, 0)
Or as I would write it:
#
# @throws ZeroDivisionError Does not work with zeros.
#
def bar(a):
return foo(a, 0)
(But which syntax you exactly rely on for documentation is a completely different matter and beyond the scope of this question.)
There are situations when catching exceptions within a function/method are a good practice. For example this is the case if you want a method to succeed in any way even if some internal operation might fail. (E.g. if you try to read a file and if it does not exist you want to use default data.) But catching an exception just in order to raise it again typically does not make any sense: Right now I can't even come up with a situation where this might be useful (though there might be some special cases). If you want to provide information that such an exception could be raised, do not rely on users looking into the implementation but rather into the documentation of your function/method.
Outputting Errors
In any way I would not follow your approach of just printing a simple error message:
try:
bar(6)
except ZeroDivisionError:
print("Error!")
It is quite labor-intensive to come up with reasonable, human readable, simple error messages. I used to do this but the amount of code you need for that approach is immense. To my experience it is better to just fail and print out the stack trace. With this stack trace typically anyone can find the reason for the error very easily.
Unfortunately Python does not provide a very readable stack trace in error output. To compensate for this I implemented my own error output handling (reusable as a module) that even makes use of colors, but that's a different matter and might be a bit beyond the scope of this question as well.