Exception handling is a crucial aspect of writing robust and reliable Python code. Whether you’re a beginner or an experienced developer, getting an error, or exception, in your Python program means the entire program will crash. You don’t want this to happen in real-world programs. Instead, you want the program to detect errors, handle them, and then continue to run. In this blog, we’ll explore the fundamentals of exception handling in Python, including syntax, best practices, and advanced techniques.
What Are Exceptions?
Exceptions are runtime errors that disrupt the normal flow of a program. For example, trying to open a non-existent file, dividing by zero, or accessing an invalid index in a list will raise exceptions. If unhandled, these exceptions cause your program to crash.
Basic Syntax: try and except
The primary mechanism for handling exceptions in Python is the try-except block. Errors can be handled with with this. The code that could potentially have an error is put in a try clause. The program execution moves to the start of a following except clause if an error happens.
Here’s the basic structure:
def cal(value):
try:
return 10 / value
except ZeroDivisionError:
print("Cannot divide by zero!")
print(cal(0))
print(cal(2))
print(cal(3))
How It Works:
- The code inside the
tryblock is executed. - If an exception occurs, Python checks the
exceptblocks for a matching exception type. - If a match is found, the corresponding
exceptblock runs.

Catching Specific Exceptions
Always catch specific exceptions to avoid silencing unexpected errors. Python has many built-in exceptions (e.g., ValueError, TypeError, FileNotFoundError).
import math
x = int(input('Please enter a positive number: '))
try:
print(f'Square Root of {x} is {math.sqrt(x)}')
except ValueError:
print('Number is less than 0')
Output

The else Clause
The else block runs only if no exceptions were raised in the try block. Use it to separate “happy path” code from error handling.
import math
def sqr(value):
try:
x = math.sqrt(value)
except ValueError:
print('Number is less than 0')
else:
print(f'The Answer is: {x}')
value = int(input('Please enter a positive number: '))
sqr(value)
Output

The finally Clause
The finally block runs regardless of whether an exception occurred. It’s ideal for cleanup tasks (e.g., closing files or releasing resources).
import math
def sqr(value):
try:
x = math.sqrt(value)
except ValueError:
print('Error')
else:
print(f'The Answer is: {x}')
finally:
print('Program Ends')
value = int(input('Please enter a positive number: '))
sqr(value)
Output

Raising Exceptions Manually
Use the raise keyword to trigger exceptions intentionally. This is useful for enforcing constraints.
def validate_age(age):
if age < 0:
raise ValueError("Age cannot be negative!")
return age
try:
validate_age(-5)
except ValueError as e:
print(e)
Creating Custom Exceptions
Define custom exceptions by subclassing Python’s built-in Exception class. This makes your code more readable and errors more descriptive. (note: I have used RegEx, for that blog will be out soon :) )
import re
class InvalidEmailError(Exception):
"""Raised when an email format is invalid."""
pass
def send_email(valid,email):
if not valid:
raise InvalidEmailError(f"Invalid email: {email}")
email = input('Please enter your email: ')
valid = re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email)
try:
send_email(valid, email)
except InvalidEmailError as e:
print(e)
Output

Logging a Python Error
We can Log an exception in Python with an error. This can be done in the logging.exception() method. This function logs a message with level ERROR on this logger.
import math
import logging
def sqr(value):
try:
x = math.sqrt(value)
except ValueError:
logging.exception("Error")
else:
print(f'The Answer is: {x}')
finally:
print('Program Ends')
value = int(input('Please enter a positive number: '))
sqr(value)
Output

Best Practices for Exception Handling
- Catch specific exceptions: Avoid broad
except:clauses that hide bugs. - Keep try blocks minimal: Only wrap code that might raise an exception.
- Use
finallyfor cleanup: Ensure resources are released (e.g., closing files). - Log exceptions: Use
logging.error()instead ofprint()for production code. - Provide meaningful messages: Help debug issues faster with clear error descriptions.
- Avoid empty
exceptblocks: Silent failures make debugging harder.
Conclusion
Exception handling is essential for writing better Python applications. By using try-except blocks effectively, catching specific errors, and with else/finally clauses, you can create programs that handle unexpected scenarios.
Now go forth and write bulletproof Python code!