Python Decorators: A Comprehensive Guide with Practical Examples
This article explains Python decorators, covering their purpose and providing ten practical examples—including timing, logging, caching, type checking, singleton, retry, profiling, authentication, asynchronous, and input validation—each illustrated with complete, ready‑to‑run code snippets.
Python decorators are special functions that can modify or enhance other functions without altering the original source code, enabling features such as logging, performance testing, permission checks, caching, and transaction handling.
1. Timing Decorator (@timer) – Measures function execution time.
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} executed in {end_time - start_time} seconds")
return result
return wrapper
@timer
def example_function():
time.sleep(1)
example_function()2. Logging Decorator (@log_execution) – Records function calls and results.
import logging
logging.basicConfig(level=logging.INFO)
def log_execution(func):
def wrapper(*args, **kwargs):
logging.info(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
result = func(*args, **kwargs)
logging.info(f"{func.__name__} returned: {result}")
return result
return wrapper
@log_execution
def add(a, b):
return a + b
add(3, 5)3. Caching Decorator (@lru_cache) – Uses functools.lru_cache to cache results and avoid repeated calculations.
from functools import lru_cache
@lru_cache(maxsize=None)
def fibonacci(n):
if n < 2:
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10))4. Type‑Check Decorator (@type_check) – Ensures function arguments match expected types.
def type_check(*arg_types):
def decorator(func):
def wrapper(*args, **kwargs):
for arg, expected_type in zip(args, arg_types):
if not isinstance(arg, expected_type):
raise TypeError(f"Argument {arg} is not of type {expected_type}")
return func(*args, **kwargs)
return wrapper
return decorator
@type_check(int, int)
def multiply(x, y):
return x * y
print(multiply(2, 3))5. Singleton Decorator (@singleton) – Guarantees a class has only one instance.
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Singleton(metaclass=SingletonMeta):
pass
s1 = Singleton()
s2 = Singleton()
print(s1 is s2)6. Retry Decorator (@retry_on_exception) – Automatically retries a function when specified exceptions occur.
import random, time
def retry_on_exception(max_retries=3, exceptions=(Exception,), delay=1):
def decorator(func):
def wrapper(*args, **kwargs):
retries = 0
while retries < max_retries:
try:
return func(*args, **kwargs)
except exceptions as e:
print(f"Caught {e}. Retrying...")
retries += 1
time.sleep(delay)
raise Exception("Max retries exceeded.")
return wrapper
return decorator
@retry_on_exception(max_retries=3)
def might_fail():
if random.randint(0, 2) == 0:
raise ValueError("Failed.")
print("Success!")
might_fail()7. Performance‑Profiling Decorator (@profile) – Uses cProfile to profile function execution.
import cProfile
def profile(func):
def wrapper(*args, **kwargs):
profiler = cProfile.Profile()
profiler.enable()
result = func(*args, **kwargs)
profiler.disable()
profiler.print_stats()
return result
return wrapper
@profile
def dummy_function(n):
sum([i**2 for i in range(n)])
dummy_function(100000)8. Authentication Decorator (@authenticate) – Checks whether a user is logged in before allowing function execution.
def authenticate(user_required=True):
def decorator(func):
def wrapper(*args, **kwargs):
if not user_required or user_is_logged_in():
return func(*args, **kwargs)
else:
raise PermissionError("User not authenticated.")
return wrapper
return decorator
@authenticate
def sensitive_operation():
print("Sensitive operation performed.")
sensitive_operation()9. Asynchronous Decorator (@async_decorator) – Wraps async functions to add pre‑ and post‑execution behavior.
import asyncio
async def async_decorator(func):
async def wrapper(*args, **kwargs):
print("Starting async task...")
result = await func(*args, **kwargs)
print("Async task finished.")
return result
return wrapper
@async_decorator
async def long_running_task():
await asyncio.sleep(2)
return "Task done."
asyncio.run(long_running_task())10. Input‑Validation Decorator (@validate_input) – Ensures function arguments fall within a specified range.
def validate_input(minimum=0, maximum=100):
def decorator(func):
def wrapper(value):
if not (minimum <= value <= maximum):
raise ValueError(f"Value must be between {minimum} and {maximum}")
return func(value)
return wrapper
return decorator
@validate_input(1, 10)
def process_value(value):
print(f"Processing value: {value}")
process_value(5)Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
