Course progress Course outline 24 of 24 lessons available
Python Foundations
Data and Collections
Building Reliable Programs
Modeling with Objects
Professional Python
Advanced Python
An exception is an alarm
Chapter 9 gave each function a contract. What if an input breaks that promise? Python stops the current path and raises an exception. That is useful information, not a monster.
- Stop an operation cannot finish
- Read start at the last line
- Name it type plus message
- Choose fix, reject, or recover
This line intentionally fails:
minutes = int("forty")
The traceback ends with a ValueError message. Read that final line first, then move upward to the line in your file. Different types point to different broken promises: int(None) raises TypeError, and 10 / 0 raises ZeroDivisionError.
Catch only the alarm you understand
A try block should be tiny. Put only the risky operation you intend to handle inside it.
raw_minutes = "forty"
try:
minutes = int(raw_minutes)
except ValueError:
print("Minutes must be a whole number.")
else:
print("Accepted:", minutes)
Output:
Minutes must be a whole number.
The except ValueError is narrow: it handles the conversion failure it understands. Avoid bare except: and avoid catching Exception merely to keep moving. They can hide spelling mistakes and broken calculations.
Keep later formatting in else. Then a bug in that formatting cannot be mislabeled as bad input. Catch several types together only when the boundary truly treats them the same, such as except (TypeError, ValueError) for text-or-None input.
Four paths have four jobs
- try attempt one risky step
- except handle one expected failure
- else use a successful result
- finally always clean up
def show_average(total, count):
try:
average = total / count
except ZeroDivisionError:
print("No sessions to average.")
else:
print(f"Average: {average:.1f}")
finally:
print("Calculation finished.")
show_average(90, 3)
show_average(0, 0)
Output:
Average: 30.0
Calculation finished.
No sessions to average.
Calculation finished.
else runs only after success. finally runs after success, a caught exception, or an uncaught exception. Use it for real cleanup, not to hide an error or replace a return value. Chapter 12 will show a simpler cleanup tool for files.
Validation is a gate, not a guess
Converting a value and checking its meaning are two separate gates. "0" can become the integer 0, but zero may still be forbidden.
- Raw input "45" or "many"
- Convert can it become an integer?
- Validate is 1 through 1440 allowed?
def parse_minutes(raw_minutes):
try:
minutes = int(raw_minutes)
except (TypeError, ValueError) as cause:
raise ValueError("minutes must be a whole number") from cause
if not 1 <= minutes <= 1440:
raise ValueError("minutes must be from 1 to 1440")
return minutes
raise ... from cause gives callers a stable, helpful message while preserving the original conversion failure in error.__cause__ and the traceback.
Use TypeError when the kind of object breaks the interface, and ValueError when the kind is accepted but its value is not. Do not silently invent zero for required bad data. Small edge: int(True) becomes 1; reject Boolean values explicitly if your contract says they are not minutes.
Build a trusted study gate
Save this complete project as study_gate.py:
def normalize_topic(raw_topic):
if not isinstance(raw_topic, str):
raise TypeError("topic must be text")
topic = raw_topic.strip()
if not topic:
raise ValueError("topic must not be empty")
return topic
def parse_minutes(raw_minutes):
if isinstance(raw_minutes, bool):
raise TypeError("minutes must not be a Boolean")
try:
minutes = int(raw_minutes)
except (TypeError, ValueError) as cause:
raise ValueError("minutes must be a whole number") from cause
if not 1 <= minutes <= 1440:
raise ValueError("minutes must be from 1 to 1440")
return minutes
def make_session(raw_topic, raw_minutes):
return {
"topic": normalize_topic(raw_topic),
"minutes": parse_minutes(raw_minutes),
}
def process_entries(raw_entries):
accepted = []
problems = []
for number, (topic, minutes) in enumerate(raw_entries, start=1):
try:
session = make_session(topic, minutes)
except (TypeError, ValueError) as error:
problems.append(f"Entry {number}: {error}")
else:
accepted.append(session)
return accepted, problems
def format_report(sessions, problems):
total = 0
for session in sessions:
total += session["minutes"]
lines = [
f"Accepted: {len(sessions)}",
f"Rejected: {len(problems)}",
f"Trusted minutes: {total}",
]
for problem in problems:
lines.append("- " + problem)
return "\n".join(lines)
def run_checks():
assert normalize_topic(" Python ") == "Python"
assert parse_minutes("45") == 45
try:
parse_minutes("many")
except ValueError as error:
assert str(error) == "minutes must be a whole number"
assert isinstance(error.__cause__, ValueError)
else:
raise AssertionError("bad minutes were accepted")
run_checks()
raw_entries = [
(" Python ", "45"),
("Git", "many"),
(" ", "15"),
("SQL", "30"),
("Testing", "0"),
]
sessions, problems = process_entries(raw_entries)
print("Checks passed.")
print(format_report(sessions, problems))
Run python3 study_gate.py:
Checks passed.
Accepted: 2
Rejected: 3
Trusted minutes: 75
- Entry 2: minutes must be a whole number
- Entry 3: topic must not be empty
- Entry 5: minutes must be from 1 to 1440
The collection boundary catches only the validation failures it expects. A programming defect still stays visible instead of being called “bad input.”
Three tiny missions
- Write
parse_rating()for whole numbers 1 through 5 and test 0, 1, 5, and 6. - Remove the
minuteskey from a dictionary; catch onlyKeyError, and put successful printing inelse. - Add
(42, "10")and("Git", True)to the project data and predict both messages.
Ready for Chapter 11?
- I read a traceback from its final line.
- I keep
trysmall and catch only expected exception types. - I can explain
except,else, andfinally. - I convert first, then validate meaning.
- I use
raise ... from ...to preserve a cause. - I ran the Study Gate and all checks passed.
Next, you will move these trusted helpers into modules and packages and give each project its own environment.