Vocabulary for this course

The terms the chapters assume, in plain words. You can open this from any chapter.

argument
A value you put inside the parentheses when you call a function, which the function then receives.
block
A group of lines that belong together under one opening line, such as the body of an if or a loop.
branch
A path a program can take when a condition sends it one way or another.
call site
The place in the code where a function is called, as opposed to where it is defined.
class
A template that bundles data with the code that works on it; each copy has its own values.
dictionary
A collection of key and value pairs, where a key is used to look up its value.
exception
An error that stops the normal flow of a program unless some code catches it.
function
A named piece of code that does one job and can be called whenever you need it.
function object
A function treated as a value, so it can be stored in a variable, passed to another function, or returned.
indentation
The spaces at the start of a line that show which block it belongs to.
integer
A whole number with no decimal point, such as 3, 0 or -12.
list
An ordered collection of values that you can grow, shrink and reach into by position.
parameter
A name in a function definition that stands for the value the caller will supply.
return value
The result a function hands back to the code that called it.
scope
The part of a program where a name can be used; a name made inside a function is not visible outside it.
traceback
The report Python prints when a program fails, listing the calls that led to the error.
variable
A name that refers to a value, so you can use that value again later or change it.

Python from the ground up

When things go wrong

How to read a traceback without panic, the three errors you will meet first, and catching the ones you can answer.

Part 8 of 15

Intermediate15 min readBack to Courses

The report at the bottom

When a line cannot run, Python stops there and prints a report called a traceback. It looks alarming and reads simply, as long as you start from the bottom.

The last line names the problem: the kind of error, and a sentence about what it wanted. The line above points at the place — a line number and a copy of the code. Read the last line, then look at the line number it mentions, and ignore the middle until you need it.

Three you will meet first

  • NameError — a name that does not exist. Nine times out of ten it is text missing its quotes, or a variable spelled differently two lines apart.
  • TypeError — two values that cannot be used together, like text and a number with +. The fix is deciding which one should change.
  • ValueError — the type is right and the value is impossible: int("twenty") is asked to turn words into a number.

Catching what you can handle

Some failures are not bugs, they are news: a visitor typed a word where a number was asked for. try runs the risky lines, and except answers the error you expected — the program continues instead of stopping.

The except line names which error it answers. except ValueError: catches a bad value and lets every other kind of problem through, which is what you want: an error you did not expect is information, not noise to hide.

A bad value, answered

text = "twenty"

try:
    age = int(text)
    print(f"Age: {age}")
except ValueError:
    print("That is not a number.")

Catch narrowly, or not at all

A bare except: — with nothing after it — catches everything, including the mistakes you made in your own program. It turns a helpful traceback into silence, and silence is the hardest thing to debug.

Name the error you can answer, and leave the rest to stop the program loudly. The loud version takes a minute to read; the silent version can take an afternoon.

A number that might not be one

Right now the program stops with a ValueError. Wrap the conversion so bad text prints a friendly line instead — then change the text to a real number and run it again.

Output
Click "Run" to execute your code

The middle of the traceback is a path

The frames above the error are the calls that led to it, oldest at the top and newest at the bottom. For a one-line program there is nothing to read. For a program with functions, the frame just above the error names the function that was running, and the one above that names its caller.

That path is the useful part when the failing line looks innocent. A conversion that fails on a value passed in from elsewhere points at the conversion, but the mistake may be in the caller that supplied the value. Read upward until the values stop making sense.

An error message is written for the person debugging

The sentence after the error type is not decoration. invalid literal for int() with base 10: 'twenty' names the function, the base and the exact text it refused. Reading it slowly often saves a print statement, because it already says which value was wrong.

When you write your own handler, keep that habit: say what was expected and what arrived. A message like “bad input” sends the next reader back to the code; “expected a number, got twenty” ends the investigation.

Read the last line first. It names the problem; the line above it points at the place. The middle of the traceback is only interesting when your own functions call each other.

An error travels up until something catches it

When a line fails, Python does not jump to the next line. It stops that function and hands the error to whoever called it, then to their caller, until some try names that kind of error or the program runs out of callers and stops.

That is why the traceback lists several places: the bottom is where the error happened, and the frames above are the calls that led there. It is also why a try only protects the lines inside it — an error raised after the block, or in a different function that does not catch it, keeps travelling.

Naming the error object

except ValueError as error: gives the exception object a name, so the handler can print or re-raise it. The object carries the message Python wrote, which is often more specific than the type alone.

finally runs whether the try succeeded or failed, which is the place for cleanup. else runs only when no error happened. Most programs need only try and except, but the two extra clauses make the intent exact.

Three ways a try makes debugging harder: a bare except: catches your own typos as well as the error you expected; a try wrapped around too much code blames the wrong line; and an except that prints nothing turns a loud failure into silence.

Try these next

  • Catch ZeroDivisionError around 10 / 0 and print a sentence instead.
  • Put two conversions in one try, make the second fail, and work out which line the traceback names.
  • Remove the except and read the traceback from the bottom up before putting it back.

A division that can fail

`divide(10, 0)` would stop the program. Add `try` and `except ZeroDivisionError` inside the function so it returns `None`, then call it with a good and a bad denominator and print a plain sentence for each.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

Which error does int("twenty") raise?