Chapters
Python114 chapters

Files and errorsChapter 83 of 114

Try...Except

Handle the errors you expect, and let the rest surface.

The basic form

Python
try:
    value = int("not a number")
except ValueError:
    print("that was not a number")

Output

that was not a number

The try block runs. If it raises something the except names, that block runs instead and the program carries on.

Catch what you expect

Naming the exception is the whole point. A bare except: swallows everything, including your own typos and the user's Ctrl+C:

Python
values = ["10", "x", "30"]
total = 0

for text in values:
    try:
        total += int(text)
    except ValueError:
        print("skipping", text)

print(total)

Output

skipping x
40

Seeing the error

Python
try:
    int("abc")
except ValueError as problem:
    print("failed:", problem)
    print(type(problem).__name__)

Output

failed: invalid literal for int() with base 10: 'abc'
ValueError

Several exceptions

Python
def divide(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return "cannot divide by zero"
    except TypeError:
        return "those are not numbers"

print(divide(10, 2))
print(divide(10, 0))
print(divide(10, "x"))

Output

5.0
cannot divide by zero
those are not numbers

One block can catch several with a tuple:

Python
try:
    int("abc")
except (ValueError, TypeError) as problem:
    print("bad input:", type(problem).__name__)

Output

bad input: ValueError

else and finally

else runs when nothing was raised. finally runs either way, and is for cleanup:

Python
def parse(text):
    try:
        value = int(text)
    except ValueError:
        print("could not parse", text)
    else:
        print("parsed", value)
    finally:
        print("done with", text)

parse("42")
parse("x")

Output

parsed 42
done with 42
could not parse x
done with x

Keeping the try block to the line that can fail, and putting the rest in else, means you cannot accidentally catch an exception from code you were not testing.

finally always runs

Even when the function returns from inside the try:

Python
def read():
    try:
        return "value"
    finally:
        print("cleanup happened")

print(read())

Output

cleanup happened
value

That is what with uses underneath to close your files.

Exceptions are classes

They sit in a hierarchy, and catching a parent catches its children:

Python
print(issubclass(FileNotFoundError, OSError))
print(issubclass(ValueError, Exception))

try:
    open("missing.txt")
except OSError as problem:
    print("caught as OSError:", type(problem).__name__)

Output

True
True
caught as OSError: FileNotFoundError

Order your except blocks from most specific to most general, or the general one catches everything first.

The ones you will meet

ExceptionRaised when
ValueErrorright type, wrong value
TypeErrorwrong type entirely
KeyErrordictionary key missing
IndexErrorlist position out of range
FileNotFoundErrorthe file is not there
ZeroDivisionErrordivided by zero
AttributeErrorno such attribute
NameErrorname is not defined

Do not catch what you cannot handle

If there is nothing sensible to do, let it propagate. A crash with a traceback is far more useful than silence:

Python
def load(text):
    return int(text)

try:
    load("abc")
except ValueError:
    print("the caller decided what to do about it")

Output

the caller decided what to do about it

The function itself did not swallow the problem. Handling belongs where there is enough context to choose a response.

Test yourself

2 questions

What is wrong with a bare 'except:' block?

Show the answer

It hides your own bugs, such as a misspelled name — Catch the specific exception you expect, and let everything else surface.

When does the else clause of a try run?

Show the answer

When the try block raised nothing — Keeping the try to just the risky line and the rest in else stops you catching the wrong exception.

Next chapter

Raising Exceptions

Signal a problem yourself, and define your own exception types.