Errors and exceptions

Introduction

Every programmer sees errors every day. An error message is not a punishment; it tells you what went wrong and where. This lesson shows how to read one, and how to handle problems you expect with try and except.

Things go wrong in every real program: a user types nonsense, a file is missing, a value is zero. Handling these situations is what separates a program that crashes from one that says "please enter a number" and carries on.

How it works

Reading an error

nums = [10, 20, 30]
print(nums[3])
print("This line never runs")
Open in compiler

Step to the end. The red banner shows three things:

  1. The type, here IndexError, which names the kind of problem.
  2. The message, list index out of range, the details.
  3. The line, 2, where it happened. The visualizer marks that line in the editor.

Line 3 never runs: an uncaught error stops the program at once.

There are two kinds of error. A syntax error (such as a missing colon) is found before anything runs. A runtime error (such as dividing by zero) happens while the program runs, at one particular step.

Handling an expected error

text = input("Enter a number: ")
try:
    n = int(text)
    print(n * 2)
except ValueError:
    print("That is not a whole number")

Input used: abc

Open in compiler

int("abc") raises a ValueError. Because it happens inside try:, Python jumps to the matching except ValueError: block instead of stopping. Step through it: the error appears as one step, then the except block runs and the program finishes normally. This example was recorded with the input abc; try a real number too.

Syntax

try:
    n = int(text)
except ValueError:
    print("Not a whole number")

Example

def safe_divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return None

print(safe_divide(10, 2))
print(safe_divide(1, 0))
Open in compiler

safe_divide(1, 0) would divide by zero, but the function catches ZeroDivisionError and returns None instead.

Line by line

  1. def safe_divide(a, b): defines the function.
  2. return a / b is inside try, because dividing can fail.
  3. safe_divide(10, 2) works and gives 5.0.
  4. safe_divide(1, 0) raises ZeroDivisionError at the return line.
  5. Python jumps to the matching except, and the function returns None.

Expected result

5.0
None

In the real world

Network requests fail, files go missing and users make typing mistakes, so production code plans for all of these. A good message at the right moment is often the difference between a usable app and an abandoned one.

Common mistakes

  • Catching everything with a bare except:. It hides real bugs; name the exception you expect.
  • Putting too much code inside try. Keep only the line that can fail there.
  • Ignoring the line number. Start debugging at the line the error points to, then look at the values just before it in the visualizer.

Best practices

  • Catch only errors that you know how to handle.
  • Keep the try block as small as you can.
  • Read the error's type, message and line before changing any code.

Summary

Errors have a type, a message and a line. An uncaught error stops the program, and try and except handle the ones you expect. Specific handlers keep real bugs visible.

Practice

Read two lines and print the first number divided by the second. Print Cannot divide by zero for a zero divisor and Invalid input if either line is not a number.

Quick check

1. What happens to the lines after an uncaught error?
2. Which exception does int('12a') raise?
3. When does the except block run?

Key takeaways

  • An uncaught error stops the program.
  • Name the exceptions that you expect.
  • The type, message and line tell you where to look.

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.