
Decorators let you add reusable logic to functions without changing the core code. They are wrappers that enhance the functions — perfect for logging, authentication, caching, or timing execution.
But what is a Decorator?
A decorator is simply a function that takes another function as an argument, adds some behavior, and returns a new function.
A small example -
def first_decorator(func):
def wrapper():
print("Function starting")
func()
print("Function ended")
return wrapper
@first_decorator
def helloworld():
print("Hello, World!")
helloworld()
And the output -
Function starting
Hello, World!
Function ended
A Practical Example
import time
import logging
logging.basicConfig(level=logging.INFO)
def log_api_call(func):
def wrapper(*args, **kwargs):
start = time.time()
logging.info(f"Calling: {func.__name__}")
result = func(*args, **kwargs)
end = time.time()
logging.info(f"Completed {func.__name__} in {end - start:.2f}s")
return result
return wrapper
@log_api_call
def fetch_user_data(user_id):
time.sleep(1) # Simulate API call
return {"id": user_id, "name": "John Doe"}
@log_api_call
def fetch_orders():
time.sleep(2) # Simulate another API call
return [{"order_id": 1, "amount": 100}]
fetch_user_data(42)
fetch_orders()
And the output -
INFO:root:Calling: fetch_user_data
INFO:root:Completed fetch_user_data in 1.00s
INFO:root:Calling: fetch_orders
INFO:root:Completed fetch_orders in 2.00s
Now, every API function logs automatically — no code duplication.
