When something goes wrong in Python, it raises an exception. Instead of letting our program crash, we can catch that exception and handle it gracefully. That’s what try/except is for.
The Basics: try/except
We wrap the risky code in a try block and handle the error in except.
try:
result = 10 / 0
except ZeroDivisionError:
print("Can't divide by zero!") # this runs instead of crashing
Catching Specific Exceptions
We should always catch specific exceptions. This way we know exactly what went wrong.
try:
num = int("hello")
except ValueError:
print("That's not a valid number!")
We can have multiple except blocks for different error types:
try:
data = {"name": "Manish"}
print(data["age"])
except KeyError:
print("Key doesn't exist")
except TypeError:
print("Wrong type used")
The else Clause
The else block runs only when no exception occurred. Think of it as the “happy path” code.
try:
num = int("42")
except ValueError:
print("Invalid number")
else:
print(f"Parsed successfully: {num}") # runs because no error happened
The finally Clause
finally always runs — whether an exception happened or not. It’s perfect for cleanup tasks like closing files or database connections.
try:
f = open("data.txt")
content = f.read()
except FileNotFoundError:
print("File not found!")
finally:
print("This always runs — cleanup happens here")
Raising Exceptions
We can raise our own exceptions using the raise keyword. This is useful when we want to signal that something is wrong from our own code.
def set_age(age):
if age < 0:
raise ValueError("Age can't be negative")
return age
try:
set_age(-5)
except ValueError as e:
print(e) # "Age can't be negative"
Common Built-in Exceptions
Here are the ones we’ll see most often:
- ValueError — right type, wrong value (e.g.,
int("hello")) - TypeError — wrong type (e.g.,
"2" + 2) - KeyError — key not found in a dict
- IndexError — list index out of range
- FileNotFoundError — file doesn’t exist
- AttributeError — object doesn’t have that attribute
- ZeroDivisionError — dividing by zero
The Bare except Anti-pattern
Using except without specifying an exception type catches everything — including things like KeyboardInterrupt and SystemExit. This is almost always a bad idea because it hides bugs.
# Bad — don't do this
try:
something()
except:
pass # silently swallows ALL errors, even Ctrl+C
# Good — catch what we expect
try:
something()
except (ValueError, TypeError) as e:
print(f"Handled: {e}")
In simple language, exception handling is our safety net. We wrap risky code in try, catch known problems with except, run cleanup with finally, and celebrate the happy path with else.