← Master Index
Vol. 03 Module 3.4 Lecture

Decorators

Essential Python Skills for AI Engineers (added — needed in practice, not in original outline)

How This Lesson Fits the Module

Generators and *args/**kwargs prepare you for decorators—functions that wrap other functions to add cross-cutting behavior: timing, authentication, caching, retry logic, and logging. Frameworks you will use daily rely on them: Flask routes, FastAPI dependencies, PyTorch’s @torch.no_grad(), and Hugging Face training callbacks.

Decorators are not syntactic sugar alone; they are how production AI services enforce policies without cluttering business logic.

Learning Objectives

By the end of this lesson, students should be able to:

  • Explain that a decorator is a callable that takes a function and returns a modified function.
  • Read and write the @decorator syntax above function definitions.
  • Build a simple decorator using nested functions and functools.wraps.
  • Write parameterized decorators (decorators that accept arguments).
  • Identify decorators in FastAPI, Flask, and PyTorch codebases.
  • Preserve function metadata (__name__, docstrings) when wrapping.

Introduction: Wrapping Functions

A decorator applies a wrapper around a function. The @ syntax is shorthand:

@timer
def train_epoch():
    ...

# is equivalent to:
def train_epoch():
    ...
train_epoch = timer(train_epoch)
Definition — Basic Decorator Structure
from functools import wraps

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

Decorators in AI Frameworks

DecoratorFrameworkPurpose
@app.post("/predict")FastAPI / FlaskRegister HTTP route handler
@torch.no_grad()PyTorchDisable gradient tracking during inference
@lru_cachefunctoolsMemoize expensive pure functions
@retry(...)tenacity / customRetry failed API or DB calls
AI Example — Inference Guard
import torch

@torch.no_grad()
def predict(model, batch):
    return model(batch).argmax(dim=-1)

Without this decorator, PyTorch would build a computation graph during inference, wasting memory and compute.

Parameterized Decorators

When the decorator itself needs configuration, add an outer function:

def retry(max_attempts=3):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            ...
        return wrapper
    return decorator

@retry(max_attempts=5)
def call_embedding_api(text):
    ...
Common Misconception: “Decorators modify the original function in place.”

Reality: They replace the name with a wrapper. The original function still exists inside the closure, but the public binding points to wrapper. Use @wraps so tools see the original name and docstring.

Knowledge Check

  1. Short Answer: What does @deco above def f desugar to? Answer: f = deco(f).
  2. True/False: functools.wraps preserves __name__ and __doc__. Answer: True.
  3. Multiple Choice: @torch.no_grad() is mainly for: (a) training, (b) inference without gradients, (c) data loading, (d) plotting. Answer: (b).
  4. Short Answer: What is a decorator? Answer: A callable that takes a function and returns a modified (wrapped) function.
  5. True/False: Decorators modify the original function object in place. Answer: False—they replace the name with a wrapper; the original lives in the closure.
  6. Short Answer: Why use @wraps(func)? Answer: Preserve the original __name__ and docstring on the wrapper.
  7. Multiple Choice: @app.post("/predict") is used to: (a) train CNNs, (b) register an HTTP route, (c) plot loss, (d) freeze NumPy. Answer: (b).
  8. True/False: Parameterized decorators add an outer function that returns the actual decorator. Answer: True (e.g. @retry(max_attempts=5)).
  9. Short Answer: Name one stdlib decorator useful for memoizing pure functions. Answer: @lru_cache (functools).
  10. Multiple Choice: FastAPI/Flask route handlers are typically registered with: (a) decorators, (b) CSV writers, (c) PCA, (d) Hessian eigenvalues. Answer: (a).

Key Takeaways

  • Decorators wrap functions to add reusable behavior.
  • @syntax is syntactic sugar for rebinding the function name.
  • AI frameworks use decorators for routes, caching, retries, and gradient control.
  • Next: Exception Handling for robust error paths in wrapped code.
Trainer’s Guide

Layered exercise: Start with a timing decorator, add logging, then add retry. Students see how decorators compose cross-cutting concerns.

Recap: Decorators wrap functions for timing, retries, routes, and gradient control; next, handle failures with Exception Handling (try/except).