Exception Handling in Python
A program asks the user for their age and adds a year:
Python 3.13age = int(input("How old are you? ")) print("Next year you'll be", age + 1)
As long as they type a number, everything works. But the moment the user types "forty" instead of 40, int() can't turn it into a number: the program crashes with a ValueError, and the print line never runs. One bad input takes down the whole program.
Exception handling is for exactly these moments: catch the error, respond, and keep going, instead of crashing at the first surprise. We've postponed this conversation twice — in the lessons on text files and encapsulation. The time has come.
The try-except block
The risky code goes into try, the reaction to the error into except:
Python 3.13try: # code that might fail except ExceptionType: # what to do if it failed
Here's what it looks like with a real error:
Python 3.13try: result = 10 / 0 except ZeroDivisionError: print("Error: division by zero!")Error: division by zero!
The program didn't crash: Python reached the division, saw the error, jumped into except and kept going.
Different errors — different reactions
Different things can fail inside one try, and each exception type gets its own except. Python checks them in order and runs the first one that matches:
Python 3.13file_name = "data.txt" try: file = open(file_name, "r") number = int(file.readline().strip()) print(f"Result: {100 / number}") except FileNotFoundError: print(f"File {file_name} not found") except ValueError: print("The file doesn't contain a number") except ZeroDivisionError: print("The file contains zero — can't divide")File data.txt not found
If the reaction to different errors is the same, the types are listed in parentheses, separated by commas:
Python 3.13try: value = int("abc") except (ValueError, ZeroDivisionError): print("An error occurred in the calculations")An error occurred in the calculations
The else and finally blocks
else runs only if there were no errors in try:
Python 3.13try: number = int("42") except ValueError: print("That's not a number") else: print(f"Success! The number: {number}")Success! The number: 42
finally runs always — error or no error:
Python 3.13f = open("example.txt", "w") try: f.write("Hello, world!") finally: f.close() # runs even if the write above fails print("File closed")File closed
Notice that open() sits before try. Put it inside, and if the file fails to open, the variable f simply won't exist — and f.close() in finally will fail on its own. This is exactly the chore that the with statement from the files chapter automates: it closes the file for you.
finally is for whatever must happen no matter what — most often releasing resources like open files.
Information about the error: as
With as you can get the exception object itself — for example, to show its message:
Python 3.13try: result = 10 / 0 except ZeroDivisionError as e: print(f"Message: {e}")Message: division by zero
Your own errors: raise
The raise statement raises an exception manually. You've already seen it in the encapsulation lesson: the setter refused to store a negative balance. Here's how it works:
Python 3.13def check_age(age): if age < 0: raise ValueError("Age cannot be negative") print(f"Age {age} accepted") try: check_age(-5) except ValueError as e: print(f"Error: {e}")Error: Age cannot be negative
Don't catch everything at once
A bare except: with no type catches any error — including ones you never suspected, like a typo in a variable name. The program "works" while the real problem stays silent:
Python 3.13# Bad: no idea what actually happened try: number = int("abc") except: print("Some error")Some error# Good: catch exactly what you expect try: number = int("abc") except ValueError: print("Invalid number format")Invalid number format
Understanding check
Which block always runs, whether an exception occurred or not?
In the next lesson — decorators. You've already met them twice: @property in encapsulation and @abstractmethod in polymorphism. It's time to see how the line with @ works on the inside.
