Python 3home |
Introduction to Python
davidbpython.com
Introduction: unanticipated vs. anticipated exceptions
Think of exceptions that we see raised by Python (SyntaxError, IndexError, etc.) as being of two general kinds -- unanticipated and anticipated:
Examples of anticipated exceptions:
If the user enters a key, but it can't be found in the dict.
mydict = {'1972': 3.08, '1973': 1.01, '1974': -1.09}
uin = input('please enter a year: ') # user enters '2116'
print(f'value for {uin} is {mydict[uin]}')
# Traceback (most recent call last):
# File "/Users/david/test.py", line 5, in <module>
# print(f'value for {uin} is {mydict[uin]}')
# ~~~~~~^^^^^
# KeyError: '2116'
If we ask the user for a number, but they give us something else.
uin = input('please enter an integer: ')
intval = int(uin) # user enters 'hello'
print('{uin} doubled is {intval*2}')
# Traceback (most recent call last):
# File "/Users/david/test.py", line 3, in <module>
# intval = int(uin) # user enters 'seven'
# ^^^^^^^^
# ValueError: invalid literal for int() with base 10: 'hello'
If we attempt to open a file, but it has been moved or deleted.
filename = 'thisfile.txt'
fh = open(filename)
# Traceback (most recent call last):
# File "/Users/david/test.py", line 3, in <module>
# fh = open(filename)
# ^^^^^^^^^^^^^^
# FileNotFoundError: [Errno 2] No such file or directory: 'thisfile.txt'
Up to now we have managed anticipated exceptions by testing to make sure an action will be succesful.
Examples of testing for anticipated exceptions:
So far we have been dealing with anticipated exceptions by checking first -- for example, using .isdigit() to make sure a user's input is all digits before converting to int().
However, there is an alternative to "asking for permission": begging for forgiveness.
The try block can trap exceptions and the except block can deal with them.
try:
uin = input('please enter an integer: ') # user enters 'hello'
intval = int(uin) # int() raises a ValueError
# ('hello' is not a valid value)
print('{uin} doubled is {intval*2}')
except ValueError:
exit('sorry, I needed an int') # the except block cancels the
# ValueError and takes action
It's important to witness the exception and where it it is raised before attempting to trap it.
It's strongly recommended that you follow a specific procedure in order to trap an exception:
Ex. 9.12 - 9.13
Multiple exceptions can be trapped using a tuple of exception types.
companies = ['Alpha', 'Beta', 'Gamma']
user_index = input('please enter a ranking: ') # user enters '4' or 'hello'
try:
list_idx = int(user_index) - 1
print(f'company at ranking {user_index} is {companies[list_idx]}')
except (ValueError, IndexError):
exit(f'max index is {len(companies) - 1}')
The same try: block can be followed by multiple except: blocks, which we can use to specialize our response to the exception type.
companies = ['Alpha', 'Beta', 'Gamma']
user_index = input('please enter a ranking: ') # user enters '4'
try:
list_idx = int(user_index) - 1
print(f'company at ranking {user_index} is {companies[list_idx]}')
except ValueError:
exit('please enter a numeric ranking')
except IndexError:
exit(f'max index is {len(companies) - 1}')
The exception raised will be matched against each type, and the first one found will excecute its block. Ex. 9.14
When we don't specify an exception, Python will trap any exception. This is a bad practice.
ui = input('please enter a number: ')
try:
fval = float(ui)
except: # AVOID!! Should be 'except ValueError:'
exit('please enter a number - thank you')
However, this is a bad practice. Why?
(There are certain limited circumstances under which we might use except: by itself, or except Exception. One comment practice is to place the entire program execution in a try: block and to trap any exception that is raised, so the exception can be logged and the program doesn't need to exit as a result.)