The book's main argument is that the exception version of the code is better because it will catch anything that you might have overlooked if you tried to write your own error checking.
I think this statement is true only in very specific circumstances - where you don't care if the output is correct.
There is no doubt that raising exceptions is a sound and safe practice. You should do so whenever you feel there's something in the current state of the program that you (as a developer) cannot, or don't want to, deal with.
Your example, however, is about catching exceptions. If you catch an exception, you're not protecting yourself from scenarios you might have overlooked. You are doing precisely the opposite: you assume that you haven't overlooked any scenario that might have caused this type of exception, and therefore you're confident that it's alright to catch it (and thus prevent it from causing the program to exit, as any uncaught exception would).
Using the exception approach, if you see ValueError
exception, you skip a line. Using the traditional non-exception approach, you count the number of returned values from split
, and if it's less than 2, you skip a line. Should you feel more secure with the exception approach, since you may have forgotten some other "error" situations in your traditional error check, and except ValueError
would catch them for you?
This depends on the nature of your program.
If you're writing, for example, a web browser or a video player, a problem with inputs should not cause it to crash with an uncaught exception. It's far better to output something remotely sensible (even if, strictly speaking, incorrect) than to quit.
If you're writing an application where correctness matters (such as business or engineering software), this would be a terrible approach. If you forgot about some scenario that raises ValueError
, the worst thing you can do is to silently ignore this unknown scenario and simply skip the line. That's how very subtle and costly bugs end up in software.
You might think that the only way you can see ValueError
in this code, is if split
returned only one value (instead of two). But what if your print
statement later starts using an expression that raises ValueError
under some conditions? This will cause you to skip some lines not because they miss :
, but because print
fails on them. This is an example of a subtle bug I was referring to earlier - you would not notice anything, just lose some lines.
My recommendation is to avoid catching (but not raising!) exceptions in the code where producing incorrect output is worse than exiting. The only time I'd catch an exception in such code is when I have a truly trivial expression, so I can easily reason what may cause each of the possible exception types.
As to the performance impact of using exceptions, it is trivial (in Python) unless exceptions are encountered frequently.
If you do use exceptions to handle routinely occurring conditions, you may in some cases pay a huge performance cost. For example, suppose you remotely execute some command. You could check that your command text passes at least the minimum validation (e.g., syntax). Or you could wait for an exception to be raised (which happens only after the remote server parses your command and finds a problem with it). Obviously, the former is orders of magnitude faster. Another simple example: you can check whether a number is zero ~10 times faster than trying to execute the division and then catching ZeroDivisionError exception.
These considerations only matter if you frequently send malformed command strings to remote servers or receive zero-valued arguments which you use for division.
Note: I assume you would use except ValueError
instead of the just except
; as others pointed out, and as the book itself says in a few pages, you should never use bare except
.
Another note: the proper non-exception approach is to count the number of values returned by split
, rather than search for :
. The latter is far too slow, since it repeats the work done by split
and may nearly double the execution time.
if -1 == eachLine.find(":"): continue
, then the remainder of the loop wouldn't be indented, either.