Decorators

Guidance

  • Use functools.wraps to preserve the original function signature and metadata.
  • Keep decorator layers shallow and readable.
  • Prefer class-based decorators when you need state or extra methods.

Bad Example

import random
import time


def timer(wrapped):
    """Decorator that prints execution time."""
    def decorated(*args, **kwargs):
        start = time.time()
        ret = wrapped(*args, **kwargs)
        print("execution took: {} seconds".format(time.time() - start))
        return ret
    return decorated


@timer
def random_sleep():
    """Sleep for a short random time."""
    time.sleep(random.random())

Good Example

import functools
import time


def timer(wrapped):
    """Decorator that prints execution time."""
    @functools.wraps(wrapped)
    def decorated(*args, **kwargs):
        start = time.time()
        ret = wrapped(*args, **kwargs)
        print("execution took: {} seconds".format(time.time() - start))
        return ret
    return decorated

Sources & References