Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

Python Dictionary

  1. Home
  2. Python Dictionary
  3. def Decorator / @ Syntax

def Decorator / @ Syntax

Since: Python 2(2000)

A decorator is a mechanism that takes a function or class and returns a new function (or class) with added functionality. Writing @decorator_name immediately before a function definition applies the decorator at the time the function is defined. It is common practice to use functools.wraps() when writing your own decorators to preserve the original function's metadata.

Syntax

from functools import wraps

# Define a decorator
def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # Pre-processing
        result = func(*args, **kwargs)
        # Post-processing
        return result
    return wrapper

# Apply the decorator
@my_decorator
def my_function():
    pass

# Decorator that accepts arguments
def decorator_with_args(arg1, arg2):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        return wrapper
    return decorator

Syntax / Functions

Syntax / FunctionDescription
@decorator_nameWritten before a function definition to apply the decorator.
@wraps(func)Copies the metadata of the original func (such as __name__) to the wrapper.
*args, **kwargsA pattern used inside a decorator to accept all arguments of the wrapped function.
@deco1 @deco2Stacking decorators. They are applied from bottom to top (deco2 first, then deco1).

Sample Code

The following examples show advanced usage.

decorator.py
from functools import wraps
import time

# Decorator that measures execution time
def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"[{func.__name__}] elapsed: {elapsed:.4f}s")
        return result
    return wrapper

@timer
def heavy_process(n):
    """Simulates a heavy computation"""
    return sum(range(n))

print(heavy_process(1000000))   # [heavy_process] elapsed: 0.XXXXs

# Decorator with arguments (retry on failure)
def retry(times=3):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for i in range(times):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"Attempt {i+1}/{times} failed: {e}")
            raise RuntimeError(f"{func.__name__} failed after {times} attempts")
        return wrapper
    return decorator

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

# Stacking decorators (applying multiple decorators)
def bold(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return f"**{func(*args, **kwargs)}**"
    return wrapper

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

@bold       # Applied second (outer)
@upper      # Applied first (inner)
def greet(name):
    return f"hello, {name}"

print(greet('world'))   # **HELLO, WORLD** (upper → bold order)

# Class-based decorator
class Memoize:
    def __init__(self, func):
        self.func  = func
        self.cache = {}
        wraps(func)(self)   # Copy metadata

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

@Memoize
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

print(fib(30))  # 832040 (fast thanks to caching)
python3 decorator.py
[heavy_process] elapsed: 0.0059s
499999500000
**HELLO, WORLD**
832040

Practical Pattern: Common Web Application Decorators

web_decorators.py
from functools import wraps

# Logging decorator (records arguments and return values)
def log_calls(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"[LOG] {func.__name__} called with args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"[LOG] {func.__name__} returned {result}")
        return result
    return wrapper

# Authentication check decorator
def require_login(func):
    @wraps(func)
    def wrapper(user, *args, **kwargs):
        if not user.get('is_authenticated'):
            raise PermissionError("Login required")
        return func(user, *args, **kwargs)
    return wrapper

# Cache decorator (simple memoization)
def cache(func):
    store = {}
    @wraps(func)
    def wrapper(*args):
        if args not in store:
            store[args] = func(*args)
        return store[args]
    return wrapper

@log_calls
@require_login
def get_profile(user):
    return {"name": "Okabe Rintaro", "lab": "Future Gadget Lab"}

@cache
def expensive_calc(n):
    return sum(range(n))

user = {"is_authenticated": True}
get_profile(user)

print(expensive_calc(1000000))  # computed on first call
print(expensive_calc(1000000))  # returned from cache on subsequent calls
python3 web_decorators.py
[LOG] get_profile called with args=({'is_authenticated': True},), kwargs={}
[LOG] get_profile returned {'name': 'Okabe Rintaro', 'lab': 'Future Gadget Lab'}
499999500000
499999500000

Common mistake 1: omitting @wraps(func)

Omitting @wraps(func) causes the function name and docstring to be lost.

decorator_wraps_ng.py
from functools import wraps

# NG: Omitting @wraps(func) causes the function name and docstring to be lost.
def bad_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper  # no wraps

@bad_decorator
def greet(name):
    """Returns a greeting."""
    return f"Hello, {name}"

print(greet.__name__)  # prints 'wrapper'
print(greet.__doc__)   # prints None
python3 decorator_wraps_ng.py
wrapper
None
decorator_wraps_ok.py
from functools import wraps

# OK: Always use @wraps(func).
def good_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@good_decorator
def greet(name):
    """Returns a greeting."""
    return f"Hello, {name}"

print(greet.__name__)  # prints 'greet'
print(greet.__doc__)   # prints 'Returns a greeting.'
python3 decorator_wraps_ok.py
greet
Returns a greeting.

Common mistake 2: missing parentheses on parameterized decorator

Forgetting the parentheses on a decorator that takes arguments causes a TypeError. (retry is the decorator factory defined in the sample code section above.)

decorator_args_ng.py
# NG: Forgetting the parentheses on a decorator that takes arguments causes a TypeError.
@retry           # NG: retry is a decorator factory — parentheses are required.
def my_func():
    pass
python3 decorator_args_ng.py
Traceback (most recent call last):
  ...
TypeError: wrapper() takes 0 positional arguments but 1 was given
decorator_args_ok.py
from functools import wraps

def retry(times=3):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for i in range(times):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if i == times - 1:
                        raise
                    print(f"Retry {i + 1}/{times}: {e}")
        return wrapper
    return decorator

@retry(times=3)
def stable_func():
    print("Executed successfully")

stable_func()
python3 decorator_args_ok.py
Executed successfully

Notes

A decorator is syntactic sugar in Python. Writing @my_decorator is equivalent to func = my_decorator(func). When decorators are stacked, they are applied from the bottom up (innermost to outermost).

A decorator that accepts arguments requires three levels of nested functions, but the structure is straightforward once you understand it: the outer function receives the arguments, the middle function receives the target function, and the innermost wrapper performs the actual wrapping logic.

Decorators are ideal for implementing cross-cutting concerns such as logging, authentication checks, caching, and validation. They help avoid code duplication and support the DRY (Don't Repeat Yourself) principle.

If you find any errors or copyright issues, please .