Exceptions
Catch expected runtime errors and return useful messages without hiding unrelated bugs.
Video coming to YouTube
Work through the full lesson and run its code while the video is being prepared for publication.
Understand the concept
A syntax error prevents Python from understanding a program. An exception happens while valid code runs, such as converting 'ten' to a number or dividing by zero. A try block surrounds an operation that may fail; a matching except block handles the specific error.
Catch only the exceptions you expect. In this lesson, ValueError means numeric text could not be converted and ZeroDivisionError means the denominator was zero. Broad except clauses can hide errors you did not intend to handle.
The practice function accepts two strings because real input often begins as text. It converts them with float, divides, and returns a numeric result for valid input. For the two expected failures it returns a clear message instead of crashing.
- try runs code that may raise an exception
- except ValueError handles invalid numeric text
- except ZeroDivisionError handles a zero denominator
See it step by step
Read the code, predict the output, then compare it with the result.
01. Catch bad numeric text
try:
number = int("ten")
except ValueError:
print("Enter digits")Enter digitsint raises ValueError for this text, and the matching handler prints a useful prompt.
02. Catch division by zero
try:
print(8 / 0)
except ZeroDivisionError:
print("Choose a nonzero divisor")Choose a nonzero divisorThe handler catches only division by zero, so the program can continue.
03. Successful try skips except
try:
print(float("6") / 2)
except ValueError:
print("Invalid")3.0No ValueError occurs, so Python skips the except body.
A closer look
Follow the reasoning, inspect each result, then try the suggested changes in the console below.
Put only the risky operation in try
A try block should be narrow enough that a matching error has one clear meaning. If a large block contains several conversions and calculations, an except ValueError may catch a failure from a different line than the one you expected. Do the conversion inside try, put work that needs a valid value in else, and put any unconditional final message in finally.
The else block runs only when try finishes without an exception. The finally block runs whether conversion succeeds or fails; it is commonly used to release resources, though with open is usually clearer for files. This example prints the sequence so you can see the control flow. Change the text to '12' and predict which message disappears.
text = "not-a-number"
try:
number = int(text)
except ValueError:
print("Conversion failed")
else:
print(number * 2)
finally:
print("Attempt finished")Conversion failed
Attempt finished- int raises ValueError, so the matching except block runs.
- else is skipped, but finally still prints its message.
- With text set to '12', else prints 24 before finally.
Handle missing configuration deliberately
Not every failure is a bad number. Looking up a missing dictionary key raises KeyError. Sometimes a missing value is normal and you should use dict.get with a default; other times it means the configuration is incomplete and you should tell the caller. Decide which case applies instead of catching every exception and returning a vague message.
In this example, host is required, while port has a reasonable default. A specific KeyError handler gives a useful result for missing host. The example only formats port as text; a real network client would also need to validate its type and range before connecting. Try removing port from the first dictionary and predict the address.
def address(settings):
try:
host = settings["host"]
except KeyError:
return "Missing host"
port = settings.get("port", 8000)
return f"{host}:{port}"
print(address({"host": "localhost", "port": 9000}))
print(address({"port": 9000}))localhost:9000
Missing host- Square-bracket lookup requires host and raises KeyError if absent.
- get supplies 8000 only when optional port is absent.
- Remove port from the first call to observe the default.
Raise an error where the rule is known
Exceptions are not only accidents Python discovers. Your own function can raise one when it detects invalid input. The function knows the business rule; the caller knows how to respond. This separation keeps a reusable function from printing a warning or silently returning a fabricated value that later calculations might treat as real.
Choose an exception type and message that match the problem. ValueError fits a valid kind of argument with an unacceptable value. Catch it at the boundary where a person can correct the input. During development, avoid catching an error so broadly that unrelated bugs are disguised as ordinary validation failures. Try changing age to zero to follow the successful path.
def ticket_price(age):
if age < 0:
raise ValueError("Age cannot be negative")
return 5 if age < 18 else 10
for age in [12, -1, 20]:
try:
print(ticket_price(age))
except ValueError as error:
print(error)5
Age cannot be negative
10- The function rejects only the impossible negative age.
- The caller catches that specific error and continues to the next input.
- Set the middle age to zero and predict all three prices.
Try it in Python
Edit the example and run it. Python starts in your browser the first time you click Run.
Python console
Ready to runNeed input()? Add one value per line
Your output appears here.
Need a hint?
Inside try, convert with float and return top / bottom. Follow with two specific except clauses: ValueError and ZeroDivisionError. Each clause returns its matching message.
Complete the task and select Check task to verify your code.
Quick quiz
Three questions. You can change your answers and try again.
Typical mistakes
Everyone meets these errors. See what causes them and how to fix them.
Catching the wrong exception
try:
print(4 / 0)
except ValueError:
print('Bad number')try:
print(4 / 0)
except ZeroDivisionError:
print('Cannot divide by zero')What happens: ZeroDivisionError still stops the program.
The except type must match the error that may be raised.
Using a bare except
try:
risky_code()
except:
print('Something failed')try:
number = float('bad')
except ValueError:
print('Invalid number')What happens: Unrelated bugs are hidden behind a vague message.
Catch an expected error by name so other failures remain visible.