Learn / Programming / Python / Error Handling

Python · Lesson 12 of 15

Error Handling

try/except/finally and raising your own exceptions.

  • Intermediate
  • 13 min read
  • 3 objectives

Before this lessonLesson 11: File Handling

What you will learn

  • Catch specific exceptions
  • Use finally for cleanup
  • Raise ValueError with a message

Things go wrong: files are missing, users type letters where numbers belong, networks drop. When Python hits a problem it raises an exception. If nothing handles it, the program stops and prints a traceback. Error handling lets you respond gracefully instead.

Errors will happen, so plan for them

Users type letters where numbers are expected. Files go missing. Networks drop. A program that assumes everything goes right will crash the first time reality disagrees. Exception handling lets you say: "try this, and if a specific thing goes wrong, do this instead of crashing." It is the difference between a program that dies with a scary traceback and one that politely tells the user what to fix.

try and except

Put risky code in a try block and describe how to recover in except. Catch the specific exception you expect. A bare except: hides real bugs, including typos in your own code.

raw = "abc"
try:
    n = int(raw)
except ValueError:
    n = 0
    print("not a number, using 0")
print(n)
Output
not a number, using 0
0

else and finally

else runs only when no exception happened, and finally always runs, which makes it the place for cleanup such as closing a connection.

try:
    result = 10 / 2
except ZeroDivisionError:
    print("cannot divide by zero")
else:
    print("result:", result)
finally:
    print("done")
Output
result: 5.0
done

Raising your own errors

Use raise to signal that the caller gave you bad input. Choose the most fitting built-in type (ValueError for a bad value, TypeError for a bad type) and write a message that says what was wrong.

def parse_port(text):
    try:
        port = int(text)
    except ValueError:
        raise ValueError(f"invalid port: {text}") from None
    if not 1 <= port <= 65535:
        raise ValueError("port out of range")
    return port

print(parse_port("8080"))
print(parse_port("99999"))
Output
8080
ValueError: port out of range

Common exceptions

  • ValueError: right type, wrong value, like int("x").
  • TypeError: wrong type, like "a" + 1.
  • KeyError / IndexError: missing dictionary key or list position.
  • FileNotFoundError: the path does not exist.
  • ZeroDivisionError: dividing by zero.

try, except, else, finally: what each part is for

def parse_age(text):
    try:
        age = int(text)
    except ValueError:
        print("not a number:", repr(text))
        return None
    else:
        print("parsed fine")
        return age
    finally:
        print("(always runs)")

print(parse_age("42"))
print(parse_age("forty"))
Output
parsed fine
(always runs)
42
not a number: 'forty'
(always runs)
None
  • try: the risky code.
  • except ValueError: runs only if that error happens. Be specific.
  • else: runs only when nothing went wrong.
  • finally: always runs, good for cleanup such as closing connections.

Catch specific errors, not everything

Writing a bare except: hides real bugs, including typos. Catch the exact exception you expect, and let unexpected ones crash loudly so you notice them.

data = {"price": "12.5"}

try:
    value = float(data["price"]) / int(data.get("qty", 0))
except (KeyError, ValueError):
    print("bad input")
except ZeroDivisionError:
    print("quantity was zero")
Output
quantity was zero

Writing helpful error messages

Use raise when a caller gives you something invalid. A clear message saves the next developer (often you) hours of guessing.

def set_age(age):
    if not 0 <= age <= 130:
        raise ValueError(f"age out of range: {age}")
    return age

try:
    set_age(200)
except ValueError as err:
    print("Rejected:", err)
Output
Rejected: age out of range: 200

Worked example: ask until valid

A very common pattern: keep asking until the input is acceptable. Here the inputs are simulated with a list so you can run it.

answers = iter(["abc", "-5", "27"])

while True:
    text = next(answers)
    try:
        age = int(text)
        if age < 0:
            raise ValueError("negative")
    except ValueError:
        print(f"{text!r} is not valid, try again")
        continue
    print("Accepted", age)
    break
Output
'abc' is not valid, try again
'-5' is not valid, try again
Accepted 27

Key takeaways

  • Wrap risky code in try; handle expected problems in except.
  • Catch specific exceptions; never hide bugs with a bare except:.
  • else runs on success, finally always runs.
  • raise your own errors with clear messages.
# Write your solution here
Up next · Lesson 13Modules, Packages and pipSplit code across files, import the standard library, and install packages in a virtual environment.