Codectionary / Developer documentation / Python

Decorators

A decorator is a function that wraps another function to extend or modify its behavior, without changing its actual source code. Decorators use the @decorator_name syntax placed directly above a function definition, which is shorthand for passing the function into the decorator and reassigning the result. They're widely used for logging, timing, access control, caching, and validation.

Syntax

@decorator_name\ndef function():\n    ...

Examples

Basic Decorator

Writing a simple decorator that adds behavior before and after a function runs.

def announce(func):
    def wrapper():
        print("Starting function...")
        func()
        print("Function finished.")
    return wrapper

@announce
def say_hello():
    print("Hello!")

say_hello()
# Starting function...
# Hello!
# Function finished.

Decorator for Functions with Arguments

Using *args and **kwargs so the decorator works with any function signature.

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - start
        print(f"{func.__name__} took {elapsed:.4f}s")
        return result
    return wrapper

@timer
def slow_add(a, b):
    time.sleep(0.1)
    return a + b

print(slow_add(3, 4))

Preserving Metadata with functools.wraps

Using functools.wraps so the decorated function keeps its original name and docstring.

import functools

def log_call(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_call
def greet(name):
    """Greet someone by name."""
    return f"Hello, {name}!"

print(greet("John"))
print(greet.__name__)  # greet (not "wrapper")

Built-in Decorators

Python's standard library includes several ready-made decorators, like functools.lru_cache for memoization.

from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print(fibonacci(30))  # computed quickly thanks to caching

Best practices

  • Always use functools.wraps inside your decorator's wrapper function, so the decorated function keeps its original name, docstring, and metadata
  • Accept *args and **kwargs in the wrapper so your decorator works with functions of any signature
  • Use built-in decorators like @staticmethod, @classmethod, @property, and @functools.lru_cache before writing your own for common needs
  • Keep decorators focused on one concern (logging, timing, caching) rather than bundling multiple responsibilities into one

Runtime & versions

Python interpreter. Feature availability depends on the language and runtime version used by your project; consult the official reference below.

Python language reference

Specifications & further reading