Things go wrong in real programs. A user types abc where you wanted a number. A file has been moved. A network is down. If you do nothing, Python stops with an error message called a traceback. A good program expects some of these problems and deals with them politely. Python uses exceptions for this, and the tools are try, except, finally and raise.
What is an exception?
When something goes wrong, Python creates an exception: an object that describes the problem, such as ValueError, ZeroDivisionError, FileNotFoundError, KeyError or IndexError. If nothing handles it, the program stops. With try and except, you say: try this, and if this kind of problem happens, do that instead.
def parse_amount(text):
"""Turn text like '1,250.50' into a number, or raise a clear error."""
cleaned = text.strip().replace(",", "")
try:
value = float(cleaned)
except ValueError:
raise ValueError(f"'{text}' is not a number") from None
if value < 0:
raise ValueError("Amount cannot be negative")
return value
for sample in ["1,250.50", "abc", "-5", " 40 "]:
try:
print(parse_amount(sample))
except ValueError as error:
print("Error:", error)
try:
print(10 / 0)
except ZeroDivisionError:
print("Cannot divide by zero")
try:
with open("no_such_file.txt", encoding="utf-8") as f:
f.read()
except FileNotFoundError as error:
print("File problem:", error.filename)
finally:
print("This always runs")
numbers = [1, 2]
try:
print(numbers[5])
except IndexError as error:
print(type(error).__name__, "-", error)
class BudgetError(Exception):
pass
try:
raise BudgetError("Over budget by 300")
except BudgetError as error:
print("Caught:", error)
1250.5
Error: 'abc' is not a number
Error: Amount cannot be negative
40.0
Cannot divide by zero
File problem: no_such_file.txt
This always runs
IndexError - list index out of range
Caught: Over budget by 300
Reading the example
parse_amountcleans the text, triesfloat(...)and raises its ownValueErrorwith a message that a user can understand.from Nonehides the confusing first error. It also rejects negative numbers by raising an error. The program prints the error withprint("Error:", error).- In
try, put only the line or lines that might fail. Inexcept ValueError as error:, name the specific error you expect, and useas errorto get the details. ZeroDivisionErroris caught so the program goes on.- For the missing file,
error.filenametells us which file. Thefinallyblock always runs, whether there was an error or not. Use it for clean-up, such as closing a connection. - The
IndexErrorhandler prints the name of the error type withtype(error).__name__. BudgetError(Exception)is a custom exception: a class you make for a problem in your own program. It gives a clear name to your own errors, and lets other code catch exactly that error.
Rules for good error handling
- Catch specific errors. Avoid a bare
except:orexcept Exception:that hides every problem, including your own typing mistakes. A program that silently ignores errors is very hard to fix. - Keep `try` blocks small. If the block is long, you cannot tell which line failed.
- Tell the user something useful: what went wrong and what to do.
- Do not use exceptions for normal decisions when a simple
ifis clearer. - Let unexpected errors stop the program, or log them with details. Do not hide them.
else and finally
A try can also have an else block, which runs only if no error happened, and a finally block, which always runs. A common pattern is try to read, except for the expected problem, else to continue with the data. You will not need else often in your first programs, but it is good to recognise it.
Reading a traceback
When an error is not caught, Python prints a traceback: the chain of calls that led to the error, with file names and line numbers, from the oldest to the newest. Start with the last line, which names the error and gives the message. Then look at the line just above it, which is the place where it happened in your code. The lines above that show how the program got there. Copy the last line into a search engine when you are stuck, because many people have met the same error.
Validating input in a loop
A common beginner pattern is asking again until the input is good. Combine a while True loop, try and except, and break when the value is good.
while True:
text = input("Enter your age: ")
try:
age = int(text)
except ValueError:
print("Please type a whole number, such as 25.")
continue
if 0 <= age <= 120:
break
print("Please enter an age from 0 to 120.")
print("Thanks, age saved:", age)
Your task
- Write
safe_divide(a, b)that returns the result, or printsCannot divide by zeroand returnsNonewhenbis zero. - Write the age loop above, and test it with
abc,-5,200and30. - Write
read_first_line(path)that returns the first line of a file, or the textFile not foundwhen it does not exist. - Make a custom exception
InvalidMarksand raise it when marks are below 0 or above 100.
How to check it worked
safe_divide(10, 0)prints the message and givesNone.- The age loop asks again for the three wrong inputs and accepts 30.
- You can explain why a bare
except:is a bad idea.