Topic 01

Exceptions Are Messages

Hands-on

Every error you have met so far, the ValueError from int, the KeyError from a dictionary, the FileNotFoundError from open, is an exception. It is Python's way of saying: I cannot carry on with this instruction, and here is exactly why. Once you see exceptions as messages rather than crashes, you can decide what your program should say in reply.

Think of an employee who cannot finish a task. They write a note saying what went wrong and pass it to their manager. If the manager cannot deal with it, the note goes up again, until it reaches someone who can decide, or the top of the company, where work stops. An exception travels the same way through a program.

What an Exception Is

When an instruction fails, Python creates an exception: an object with a type, such as ValueError, and a message, the sentence after the colon. Python then stops the current line and hands the exception to the code that called it, and then to the code that called that, looking for something that will deal with it. If nothing does, the program stops and prints the traceback. The next topic shows how to be the something that deals with it.

The Family

You have already met most of the everyday exceptions. Each name tells you the kind of problem before you read the message.

Exceptions you have already met
ExceptionWhat it meansExample that raises it
ValueErrorright type, unusable valueint("forty")
TypeErrorwrong type for the operation"5" + 5
NameErrora name with no valueprint(totl)
KeyErrora missing dictionary keyphones["Zoe"]
IndexErrora position that does not existitems[10]
FileNotFoundErrorno file at that pathopen("todo.txt")
ZeroDivisionErrordividing by zero10 / 0

A Traceback Through Several Functions

With functions calling functions, a traceback lists the whole chain of calls, oldest at the top and the failing line at the bottom. The program below has main, which calls average, which divides by the number of values. Given an empty list, the division fails. Read it from the bottom, as Chapter 3 taught: a ZeroDivisionError, division by zero, on the line in average that divides. Then read upward to see how the program got there: main called average, and the file called main.

chain.py
def average(values):
    return sum(values) / len(values)


def main():
    readings = []
    print(average(readings))


main()
Output
Traceback (most recent call last):
  File "/Users/you/python-course/chain.py", line 10, in <module>
    main()
    ~~~~^^
  File "/Users/you/python-course/chain.py", line 7, in main
    print(average(readings))
          ~~~~~~~^^^^^^^^^^
  File "/Users/you/python-course/chain.py", line 2, in average
    return sum(values) / len(values)
           ~~~~~~~~~~~~^~~~~~~~~~~~~
ZeroDivisionError: division by zero

The chain is valuable. It tells you not only where the program failed but how it got there, and the fix often belongs higher up than the line that failed. Here, the real question is why main passed an empty list.

Crashing Is Sometimes Right

It is tempting to think a good program never shows a traceback. It is better to think a good program never carries on with wrong data. A program that stops loudly on a real problem is doing its job; one that quietly continues and prints a wrong total is not. The rest of this chapter is about telling those cases apart.

An exception rising through the calls
the file calls main()
nothing here handles it either, so the program stops
main() calls average(readings)
the exception passes up through main
average() divides by len(values), which is 0
ZeroDivisionError is raised here
Common Confusions
  • "Exceptions are bugs." Many are expected events: a missing file, a word typed where a number was wanted. A program can and should handle those.
  • "The top of a long traceback is where it failed." The failing line is at the bottom. The lines above show how the program got there.
  • "A good program never raises exceptions." A good program raises them clearly when it cannot continue, and handles the ones it can do something about.
Why It Matters
  • Knowing the family by name makes every later error faster to understand, often before you read the message.
  • The chain of calls tells you where to look for the real cause, which is frequently one function above the one that failed.

Knowledge Check

When an exception is raised and nothing handles it, what happens?

  • It passes up through the callers, then the program stops
  • Python skips the failing line and carries on
  • The program restarts itself from its very first line
  • Python asks the person at the keyboard what it should do next

In a traceback through several functions, where is the line that actually failed?

  • At the top, just below the word Traceback itself
  • In the middle, between the functions
  • At the bottom, just above the error's name
  • It is not shown; only the function names are

Which exception does int("forty") raise?

  • TypeError, because forty is text and not a number at all
  • NameError, because the word forty is not defined
  • KeyError, because forty is not a number Python knows about
  • ValueError, because the text is not a whole number

Why can a program that stops on an error be better than one that keeps going?

  • Because stopping makes the program run faster overall
  • Carrying on with wrong data gives results that look right and are not
  • Because Python requires every program to stop the moment any error happens
  • Because a stopped program saves its data automatically

You got correct