Debugging and handling errors effectively is a must-have skill in Python. Here’s a complete, practical guide for:
🛠️ 1. Basic Python Error Handling Syntax
try:
# risky code here
result = 10 / 0
except ZeroDivisionError as e:
print(f"Caught error: {e}")
else:
print("No error occurred.")
finally:
print("Always runs, even if there’s an error.")
🔁 2. Common Python Errors and Fixes
Error | Cause | Fix Example |
---|---|---|
ZeroDivisionError | Division by zero | Check denominator before division |
TypeError | Wrong data type | Use type() checks or cast explicitly |
NameError | Variable not defined | Ensure variable is declared |
KeyError | Missing key in dict | Use .get() or in check |
IndexError | List index out of range | Check len(list) before accessing |
AttributeError | Method/attribute doesn’t exist | Use dir(obj) or check type before calling |
FileNotFoundError | File doesn’t exist | Use os.path.exists() before reading |
ValueError | Bad value (e.g., int("abc") ) | Validate input before conversion |
ImportError/ModuleNotFound | Missing import | Ensure module is installed and spelled correctly |
🔍 3. Debugging Tips
🔸 Use print()
or better: logging
import logging
logging.basicConfig(level=logging.INFO)
logging.info("Starting process...")
# Or
print("Debug here: x =", x)
🔸 Use pdb
– Python Debugger
python -m pdb script.py
Inside the debugger:
l
: list coden
: next linec
: continuep x
: print xq
: quit
Or in code:
import pdb; pdb.set_trace()
🔄 4. Catch Multiple Errors
try:
# risky stuff
except (TypeError, ValueError) as e:
print(f"Caught: {e}")
🧹 5. Create Custom Exceptions
class InvalidAgeError(Exception):
pass
def check_age(age):
if age < 0:
raise InvalidAgeError("Age cannot be negative.")
📂 6. Log Errors with Tracebacks
import traceback
try:
1 / 0
except Exception as e:
print("Something went wrong:")
traceback.print_exc()
📊 7. Best Practices Summary
✅ Do This | ❌ Avoid This |
---|---|
Use try/except/finally blocks | Swallowing errors silently |
Log exceptions using logging module | Using only print() for debugging |
Use traceback.print_exc() in handlers | Ignoring stack traces |
Create custom exceptions when needed | Raising generic Exception always |
Use pdb or IDE debuggers (VSCode, PyCharm) | Blind debugging without breakpoints |
Validate user input before parsing | Trusting all input blindly |
🧪 Example: Real-World Debug Wrapper
from functools import wraps
import traceback
import logging
def safe_execute(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
logging.error(f"Error in {func.__name__}: {e}")
traceback.print_exc()
return None
return wrapper
@safe_execute
def risky_code():
return 1 / 0
risky_code()
this safe_execute
function is a Python decorator that wraps any function to automatically catch, log, and print any exceptions, instead of letting them crash your program.
Let’s break it down line by line, with all relevant Python concepts explained:
🧱 Code Recap
from functools import wraps
import traceback
import logging
def safe_execute(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
logging.error(f"Error in {func.__name__}: {e}")
traceback.print_exc()
return None
return wrapper
🔍 Line-by-Line Explanation
✅ def safe_execute(func):
This is a decorator function — it takes a function func
as input and returns a new function (wrapper
) that adds extra behavior.
Think of decorators as functions that “wrap” other functions to enhance them without modifying their original code.
✅ @wraps(func)
Imported from functools
.
This preserves metadata like the function name and docstring when it’s decorated.
Without @wraps(func)
, the decorated function’s name would become "wrapper"
— which breaks things like introspection and debugging.
✅ def wrapper(*args, **kwargs):
This is the inner function (the “wrapper”) that will replace the original function.
*args
= all positional arguments**kwargs
= all keyword arguments
This ensures it can wrap any function, no matter what parameters it takes.
✅ try: return func(*args, **kwargs)
The original function is executed here with the same arguments.
If it works fine, it returns the result as-is.
✅ except Exception as e:
This catches all exceptions, not just specific ones.
Exception
is the base class for most built-in errors.
✅ logging.error(f"Error in {func.__name__}: {e}")
Logs the error using Python’s logging
module instead of just printing it.
func.__name__
= original function’s namee
= actual error message
✅ traceback.print_exc()
Prints the full stack trace, like what you’d see if the program crashed — very helpful for debugging.
✅ return None
If an error occurs, the wrapper fails gracefully by returning None
.
You could change this to return an empty DataFrame, False, or even re-raise the error, depending on your use case.
✅ return wrapper
The decorator finally returns the enhanced wrapper
function — which now has error handling built in.
🚀 How to Use It
@safe_execute
def divide(a, b):
return a / b
print(divide(10, 2)) # ✅ Works fine: prints 5.0
print(divide(10, 0)) # ❌ Logs error + traceback, returns None
🧠 What This Does for You
Feature | Benefit |
---|---|
@wraps | Keeps function name/docs intact |
*args, **kwargs | Makes the decorator generic |
try/except | Prevents crash, adds graceful error handling |
logging + traceback | Logs full error trace for debugging |
return None | Fails gracefully; doesn’t raise unhandled exception |
Leave a Reply