Tutorials Logic, IN info@tutorialslogic.com
Navigation
Home About Us Contact Us Blogs FAQs
Tutorials
All Tutorials
Services
Academic Projects Resume Writing Website Development
Practice
Quiz Challenge Interview Questions Certification Practice
Tools
Online Compiler JSON Formatter Regex Tester CSS Unit Converter Color Picker
Compiler Tools

Decorators in Python @decorator: Tutorial, Examples, FAQs & Interview Tips

What is a Decorator?

A decorator is a function that wraps another function to extend or modify its behavior - without changing the original function's code. They use the @ syntax.

How Decorators Work

Basic Decorator
def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Before the function")
        result = func(*args, **kwargs)
        print("After the function")
        return result
    return wrapper

# Apply with @ syntax
@my_decorator
def say_hello(name: str):
    print(f"Hello, {name}!")

say_hello("Alice")
# Before the function
# Hello, Alice!
# After the function

# Equivalent to:
# say_hello = my_decorator(say_hello)

Preserving Function Metadata

functools.wraps
from functools import wraps

def my_decorator(func):
    @wraps(func)   # preserves __name__, __doc__, etc.
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def greet(name: str) -> str:
    """Greet someone by name."""
    return f"Hello, {name}!"

print(greet.__name__)  # greet  (not 'wrapper')
print(greet.__doc__)   # Greet someone by name.

Practical Decorator Examples

Practical Examples
import time
from functools import wraps

# Timer decorator
def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"{func.__name__} took {end - start:.4f}s")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(0.1)
    return "done"

slow_function()   # slow_function took 0.1001s

# Logger decorator
def log_calls(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}({args}, {kwargs})")
        result = func(*args, **kwargs)
        print(f"{func.__name__} returned {result}")
        return result
    return wrapper

@log_calls
def add(a: int, b: int) -> int:
    return a + b

add(3, 5)
# Calling add((3, 5), {})
# add returned 8

# Retry decorator
def retry(times: int = 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(f"Failed after {times} attempts")
        return wrapper
    return decorator

@retry(times=3)
def unstable_api():
    import random
    if random.random() < 0.7:
        raise ConnectionError("Network error")
    return "Success"

Stacking Decorators

Multiple Decorators
from functools import wraps

def bold(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return f"<b>{func(*args, **kwargs)}</b>"
    return wrapper

def italic(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return f"<i>{func(*args, **kwargs)}</i>"
    return wrapper

# Applied bottom-up: italic first, then bold
@bold
@italic
def greet(name: str) -> str:
    return f"Hello, {name}!"

print(greet("Alice"))   # <b><i>Hello, Alice!</i></b>

# Class-based decorator
class Cache:
    def __init__(self, func):
        wraps(func)(self)
        self.func = func
        self._cache = {}

    def __call__(self, *args):
        if args not in self._cache:
            self._cache[args] = self.func(*args)
        return self._cache[args]

@Cache
def fibonacci(n: int) -> int:
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(35))   # fast due to caching

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.