Handle errors gracefully with try/except, run cleanup with finally, raise your own exceptions, and catch the right error types.
Why: when something goes wrong — a missing file, bad input, dividing by zero — Python "raises an exception" and stops. Handling it lets your program recover instead of crashing.
# without handling, this crashes the whole program:
result = 10 / 0
# ZeroDivisionError: division by zeroWhy: put risky code in a try block. If it raises an exception, the matching except block runs instead of crashing. Catch a specific error type so you do not accidentally hide unrelated bugs.
try:
age = int(input('Age? '))
print(f'Next year you are {age + 1}')
except ValueError:
print('Please enter a whole number.')Why: you can catch several error types, run an else block when nothing went wrong, and a finally block that always runs — perfect for cleanup like closing a file or connection.
try:
data = risky_load()
except FileNotFoundError:
print('File missing')
except PermissionError:
print('Not allowed')
else:
print('Loaded OK') # only if no exception
finally:
print('Done') # always runsWhy: use raise to signal that something is wrong in your own code — for example, when an argument makes no sense. Callers can then catch it. Choose the most fitting built-in error type.
def set_age(age):
if age < 0:
raise ValueError('age cannot be negative')
return age
try:
print(set_age(-5))
except ValueError as err:
print(f'Rejected: {err}') # Rejected: age cannot be negative