Dmitrii Korolkov

github | email

Decorators in Python

Decorators in Python are a powerful tool for modifying the behavior of functions or methods without altering their actual code. They are widely used in logging, access control, memoization, and more.


1. What is a Decorator?

A decorator is a function that takes another function as an argument and extends or modifies its behavior without changing its source code.

def my_decorator(func):
    def wrapper():
        print("Something before the function runs")
        func()
        print("Something after the function runs")
    return wrapper
@my_decorator def say_hello(): print("Hello!") say_hello()

2. Using Arguments in a Decorator

def repeat_twice(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        func(*args, **kwargs)
    return wrapper
@repeat_twice def greet(name): print(f"Hello, {name}!") greet("Alice")

3. Returning Values from a Decorated Function

def double_return(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result * 2
    return wrapper
@double_return def number(): return 10 print(number()) # Output: 20

4. Using functools.wraps to Preserve Function Metadata

from functools import wraps
def log_function(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling function {func.__name__}")
        return func(*args, **kwargs)
    return wrapper
@log_function def add(a, b): """Adds two numbers.""" return a + b print(add.__name__) # Output: add print(add.__doc__) # Output: Adds two numbers.

5. Applying Multiple Decorators

def uppercase(func):
    def wrapper():
        return func().upper()
    return wrapper
def exclamation(func): def wrapper(): return func() + "!!!" return wrapper
@uppercase @exclamation def greet(): return "hello" print(greet()) # Output: HELLO!!!

6. Class-Based Decorators

class RepeatThreeTimes:
    def __init__(self, func):
        self.func = func
    def __call__(self, *args, **kwargs):
        for _ in range(3):
            self.func(*args, **kwargs)
@RepeatThreeTimes def say_hi(): print("Hi!") say_hi()

7. Real-World Use Cases

7.1 Timing Function Execution

import time
def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} took {end - start:.4f} seconds")
        return result
    return wrapper
@timer def slow_function(): time.sleep(2) print("Function finished!") slow_function()

7.2 Caching with lru_cache

from functools import lru_cache
@lru_cache(maxsize=10)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(30))  # Much faster due to caching