You have all seen various exceptions in Python
int('hello') ValueError: invalid literal for int() with base 10: 'hello'
In the above example, when we tried to convert the string
"hello"to an integer, Python raised an exception
a = ['a', 'e', 'i', 'o', 'u'] print(a) IndexError: list index out of range
In the above example, when we tried to access the 11th thing in the list containing only 5 things, Python raised an exception
indexErrorare exceptions, but there are many more kinds of exceptions
Consider how, like the
If I am the one writing the code for converting strings to integers, what should I make my code do if someone asks my code to convert the string
"hello"to an integer?
Obviously there is no single obvious and natural way to convert the string
"hello"to an integer
Should my code simply ignore the request and carry on like nothing ever happened?
Should my code crash the whole program?
Maybe some user entered some input wrong?
Maybe the issue is with some text file that was read?
The trouble is, if I am writing the code for converting strings to integers, I cannot possibly know what you — the individual trying to use my code at some point in the future — want to do in these exceptional situations
What I can do however is raise an exception, which then communicates to the programmer using my code that they have to specify what they want to do when the exceptional situation arises
Consider the example below on dividing by zero
1def divide(a,b): 2 if b == 0: 3 raise ZeroDivisionError("Wait, that's illegal") 4 else: 5 return a/b
Obviously we’re going to have an issue if we try to divide a number by zero
But what should happen if someone tries to?
That’s entirely up to the programmer making use of the
All I need to do is communicate to them that something exceptional happened by
raisint an exception
The function first checks if
0, then the exception is
If it is not, then the function carries on as it should
18.1. Catching Exceptions
Pretend the above function
dividewas written in 1991 and you want to use it today
When you are using it today, you end up calling
divide(9,0), which is problematic
One cannot divide 9 by 0
What should the original programmer in 1991 do about it?
Well… not much considering you are the one trying to use the function today
How could the programmer know what to do to handle your specific situation today?
What the original programmer can do, however, is to add some special code that says something exceptional happened that lets you know that something is off
This then allows you, the individual trying to use
dividetoday, to deal with the situation the way you need
Read input again?
Carry on as if nothing happened?
If you are using a function that may result in an exception, you can write your code such that
tryto run the code that may or may not
The code will run normally
exceptwhen the exceptional situation arises
If the exceptional situation arises, special instructions will be specified
Below is a generalized idea of how one can do this
def my_code(): try: might cause exception except SomeError: will handle exception runs regardless
In the above example, the code in the
trywould be something that may cause an exception we want to deal with
If it turns out that the code does
raisean exception, the code within the
If no exception arises, then the
exceptblock is skipped
18.1.1. Example 1
There exists a special value for floating point numbers in Python called
NaN, which means not a number
A reasonable way to manage a
ZeroDivisionErroris to use a
1def not_a_number_example(a: float, b: float) -> float: 2 try: 3 quotient = divide(a, b) 4 except ZeroDivisionError: 5 quotient = float("NaN") 6 return quotient
divideis called and there is no
ZeroDivisionError, then the division occurs and the
On the other hand, if a
ZeroDivisionErrorhappens, we assign
quotientand return it
Either way, the
18.1.2. Example 2
Consider a program requiring a user to input some values
If this is the case, it may be ideal to have the program ask for input again if the input was inadmissible
1def continue_asking_for_input() -> float: 2 while True: 3 data = input("Provide operands for division: ").split() 4 a = float(data) 5 b = float(data) 6 try: 7 quotient = divide(a, b) 8 break 9 except ZeroDivisionError: 10 print("Cannot Divide By Zero --- Try Again") 11 12 return quotient
18.1.3. Example 3
It may be the case that if the function causes an exception, our program should stop running immediately
If this is the case, we can make use of the
exit()function to halt the program
1def stop_running_immediately(a,b) -> float: 2 try: 3 quotient = divide(a,b) 4 except ZeroDivisionError: 5 exit() # Immediately stop! 6 return quotient
Alternatively, a simpler and better implementation would be to just let the exception propagate up and have the program eventually stop as a result
1def stop_running_immediately(a,b) -> float: 2 quotient = divide(a,b) 3 return quotient
In the above example, if
dividecauses an exception, the exception would keep being handed to the calling function until it is dealt with or ultimately crashes the program
18.1.4. Example 4
Sometimes the exception may be inconsequential for the program’s functionality
Perhaps your program is rapidly reading input from some sensors for calculations and a
ZeroDivisionErroris likely caused by sensor precision
Given this, it may be the case that periodic
ZeroDivisionErrorare meaningless, so they can be ignored
1 try: 2 quotient = divide(a,b) 3 except ZeroDivisionError: 4 pass
18.2. Why Care?
Which of the above examples is the correct one?
The trouble is, that depends on your situation
The point is, how can the programmer in 1991 know what you want to do with your situation today?
Ultimately, exceptions are useful because
They allow programmers to pass info around and communicate through time
They allow us to deal with exceptional situations effectively
They provide a nice logical division between normal code and exceptional code