PYTHON Reference Guide
Revision Time: 3 mins

Decorators Reference

Wrapping functions to add behaviour — timing, logging, caching, access control.

Basic Decorator
Return: Callable

Wrap a function to execute code before and/or after it runs.

Used In: Logging, timing, input validation, retry logic.

Syntax signature:@decorator_name def func(): ...
Code snippet:
python
def logger(func):
    def wrapper(*args, **kwargs):
        print(f'Calling {func.__name__}')
        result = func(*args, **kwargs)
        print(f'Done {func.__name__}')
        return result
    return wrapper

@logger
def process(n):
    return n * 2

process(5)
Expected Output:Calling process Done process

Time Complexity: O(1) wrapping overhead

Related Methods: functools.wraps()

Remember: @logger is syntactic sugar for process = logger(process). The decorator replaces the original function.
functools.wraps()
Return: decorator

Preserve the original function's __name__ and __doc__ inside a decorator.

Used In: All production-quality decorators.

Syntax signature:from functools import wraps @wraps(func)
Code snippet:
python
from functools import wraps

def logger(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@logger
def process(): pass

print(process.__name__)
Expected Output:process

Time Complexity: O(1)

Common Mistakes: Forgetting @wraps causes debugging tools to show the wrapper name instead of the original.

Remember: Without @wraps, decorated functions show 'wrapper' in logs and tracebacks — always include it.
Decorator with Arguments
Return: Callable

Create a decorator that accepts its own configuration parameters.

Used In: Retry logic, rate limiting, permission checks.

Syntax signature:def decorator(param): def outer(func): def wrapper(*args, **kwargs): ... return wrapper return outer
Code snippet:
python
from functools import wraps

def retry(times=3):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, times + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f'Attempt {attempt} failed: {e}')
            raise RuntimeError('All retries failed')
        return wrapper
    return decorator

@retry(times=2)
def unstable_api():
    raise ConnectionError('timeout')

try:
    unstable_api()
except RuntimeError as e:
    print(e)
Expected Output:Attempt 1 failed: timeout Attempt 2 failed: timeout All retries failed

Time Complexity: O(1) overhead

Related Methods: functools.wraps()

Remember: Parameterized decorators need three levels of nesting — the outermost level accepts the decorator arguments.
Class-based Decorator
Return: Callable

Use a class with __call__ as a decorator — useful for stateful decorators.

Used In: Call counting, circuit breakers, stateful rate limiters.

Syntax signature:class Decorator: def __call__(self, func): ...
Code snippet:
python
class CallCounter:
    def __init__(self, func):
        self.func = func
        self.count = 0

    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f'Call #{self.count}')
        return self.func(*args, **kwargs)

@CallCounter
def greet(name):
    return f'Hello, {name}'

greet('Alice')
greet('Bob')
print(greet.count)
Expected Output:Call #1 Call #2 2

Time Complexity: O(1) per call

Related Methods: __call__

Remember: Class-based decorators are ideal when you need to maintain state (call count, cache, circuit breaker).
Stacking Decorators
Return: Callable

Apply multiple decorators to one function — they execute bottom-up.

Used In: Chaining middleware: auth → rate-limit → log.

Syntax signature:@dec_a @dec_b def func(): ...
Code snippet:
python
def bold(func):
    def wrapper(): return '<b>' + func() + '</b>'
    return wrapper

def italic(func):
    def wrapper(): return '<i>' + func() + '</i>'
    return wrapper

@bold
@italic
def text():
    return 'hello'

print(text())
Expected Output:<b><i>hello</i></b>

Time Complexity: O(1)

Comparison:

@bold @italic means bold(italic(text)) — innermost decorator is applied first.

Remember: Reading order is top-down but execution order is bottom-up: italic wraps text first, then bold wraps the result.
Built-in Decorators
Return: property / function / classmethod

Python ships three built-in class decorators: @property, @staticmethod, @classmethod.

Used In: Controlled attribute access, computed properties, utility functions.

Syntax signature:@property / @staticmethod / @classmethod
Code snippet:
python
class Employee:
    def __init__(self, salary):
        self._salary = salary

    @property
    def salary(self):
        return self._salary

    @salary.setter
    def salary(self, val):
        if val < 0: raise ValueError('negative')
        self._salary = val

    @staticmethod
    def currency():
        return 'USD'

emp = Employee(90000)
print(emp.salary)
print(Employee.currency())
Expected Output:90000 USD

Time Complexity: O(1)

Related Methods: @classmethod, @abstractmethod

Remember: @property turns a method into an attribute — access it without () and add validation in the setter.