= 0
x try:
1/x
except ZeroDivisionError:
print("Please don't divide by zero...")
Please don't divide by zero...
Introduction to Software Engineering (CSSE 1001)
When Python parses a file it will return a syntax error when the contents of the file do not correspond to valid Python code.
Code that passes parsing then transitions to run-time where errors or exceptions can occur. These are different than syntax-errors because you cannot detect them until they trigger.
Run-time errors are bad because they abort the execution of the program and all intermediate work is lost (unless saved to a file).
To throw/raise an error means to detect an error at runtime and react with a message.
To catch/except an error means to handle the error without aborting runtime.
Today we are going to learn how to catch exceptions so that our programs can continue running, even if they encounter an error.
The common run-time exceptions are in Table 1.
Error Type – | Description |
---|---|
AssertionError |
An assertion fails |
IOError |
File does not exist |
IndexError |
Index out of range |
KeyError |
Key in dict does not exist |
NameError |
Variable does not exist |
TypeError |
Unexpected type is given to a function |
ValueError |
Correct type, but inappropriate value |
ZeroDivisionError |
Division by zero attempted |
= [1,2,3]
xs 4] xs[
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
= {'a': 1}
xs 'b'] xs[
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'b'
"two" + 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str
2 + "two"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Note that the integer casting function has the type contract:
int(xs: str) -> int
and thus int("ten")
should not throw a type-error because "ten"
is indeed a string.
int("ten")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: 'ten'
Why do we want to catch errors? Can’t we just avoid throwing them?
Generally speaking we shouldn’t cause errors to be thrown. Practically speaking this is not possible – especially when we are interacting with the user.
try:
<code> # This code may throw an error
except ExceptionName:
<code> # This code is run when the error is of kind ExceptionName
Do not abuse this control structure. It should only be used as a last resort or in the user facing layer of your software.
This control structure is a try-catch in other languages.
Without try-except a division by zero would throw an error.
= 0
x 1/x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
= 0
x try:
1/x
except ZeroDivisionError:
print("Please don't divide by zero...")
Please don't divide by zero...
Note that we are not obligated to include the exception kind. We can catch any exception.
= 0
x try:
print(y) # this throws a name error
1/x
except:
print("You did something fishy...")
It is bad practice to essentially ignore all errors.
We can catch among a list of exceptions:
try:
print(y) # this throws a name error
1/x
except NameError:
print("You're using an undefined name...")
except ZeroDivisionError:
print("You divided by zero...")
Let us write a function
def io_double() -> int:
which takes no inputs, but rather prompts the user for a number and then doubles that number.
def io_double() -> int:
= input("Number please: ")
str_x = int(str_x)
int_x return 2*int_x
io_double() 2
Number please: 4
io_double()
Number please: two
...ValueError: invalid literal for int() with base 10: 'two'
def io_double() -> int:
while True:
= input("Number please: ")
str_x try:
= int(str_x)
int_x return 2*int_x # unreachable if line 5 errors
except ValueError:
print("That wasn't a number! Try again...")
io_double()
Number please: two't a number! Try again...
That wasnNumber please: 3
6
Alternatively, recursion (future topic) would work here as well.
def io_double() -> int:
= input("Number please: ")
str_x try:
= int(str_x)
int_x return 2*int_x
except ValueError:
print("That wasn't a number! Try again...")
# recursion io_double()
Try/Except General Framework
try:
<code>
except ExceptionName_0:
<code>
except ExceptionName_1:
<code>
.
.
.except ExceptionName_k:
<code>
Finally, if you want to raise an error (rather than catch it), do the following:
def safe_div(x: int, y: int) -> float:
"""
Return 1 / (x-y)
"""
if x==y:
raise ZeroDivisionError
return 1/(x-y)
1, 1)
safe_div(ZeroDivisionError
We can catch errors which throw at runtime with the try-except control structure.
We should only use a try-catch when absolutely necessary.