Lesson 66 +10 XP

Decorators

Decorators

A decorator takes a function and wraps it with extra behavior, without changing the function's own code.

Functions are values

In Python you can pass a function like any value:

def hello():
    print("Hello")

say = hello
say()  # Hello

A simple decorator

def decorator(func):
    def wrapper():
        print("Before the call")
        func()
        print("After the call")
    return wrapper

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

say_hello = decorator(say_hello)
say_hello()
# Before the call
# Hello!
# After the call

The @ syntax

The @ symbol applies a decorator cleanly:

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

This is exactly the same as say_hello = decorator(say_hello).

Decorators with arguments

def log(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log
def add(a, b):
    return a + b

print(add(2, 3))  # Calling add then 5

Common uses

  • Logging and timing
  • Access control (require login)
  • Caching results
  • Framework features (routes in Flask/Django)

TL;DR

  • Decorators wrap functions with extra behavior.
  • @name is sugar for func = name(func).
  • The wrapper receives args, *kwargs and calls the original.
  • Used for logging, checks, and caching.