Python Decorators
Back to LabsLearn Python decorator fundamentals using only Python and its standard library. Core workshops progress from functions as first-class objects and closures through to advanced patterns including class-based decorators, method decoration, and the descriptor protocol. Elective workshops apply these skills to practical domains such as caching, input validation, access control, and plugin systems.
Module A: Decorator Fundamentals
Core concepts of Python decorators from first-class functions and closures through to class-based decorators, the descriptor protocol, async support, metaclasses, and practical applications.
Core Workshops
A01: Functions as Building Blocks
Learn how Python treats functions as first-class objects and how closures capture state from enclosing scopes — foundational concepts for understanding decorators.
A02: Your First Decorator
Build your first Python decorators by applying the closure and factory patterns — start with manual wrapping, then graduate to the @decorator syntax.
A03: Decorator Arguments and Flexible Signatures
Build decorators that accept configuration arguments using the decorator factory pattern, and make wrappers that work with any function signature using *args and **kwargs.
A04: Preserving the Wrapped Function
Discover how decorating a function silently breaks introspection, then learn to fix it with functools.wraps — and understand the limitations that remain.
A05: Stacking Decorators
Learn what happens when multiple decorators are applied to a single function — how they compose, in what order they execute, and how to reason about the resulting wrapper chain.
A06: Class-Based Decorators
Implement decorators as classes using __init__ and __call__, and see how instance attributes provide a natural home for per-function state.
A07: Decorating Methods
Discover why decorating methods differs from decorating standalone functions — experience the breakage caused by class-based decorators on methods and write decorators that are aware of self.
A08: Decorators and the Descriptor Protocol
Understand the mechanism behind method binding — the descriptor protocol — and use it to fix class-based decorators so they work correctly on both functions and methods.
A09: Dual-Use Decorators
Build decorators that work both with and without parentheses — @decorator and @decorator(args) — using callable detection, sentinels, and functools.partial.
A17: Signature Preservation with inspect
Explore the inspect module's signature introspection tools, understand why functools.wraps does not fully preserve signatures, and appreciate why full signature preservation is hard.
A18: Context Manager Decorators
Explore the intersection of decorators and context managers — using contextlib.contextmanager and contextlib.ContextDecorator to build objects usable as both decorators and with statements.
A19: Thread Safety and Decorators
Build synchronisation decorators using threading.Lock and threading.RLock — demonstrating race conditions, per-function vs per-instance locking, and reentrant locks for recursive calls.
A20: Decorators for Async Functions
Master the challenges of decorating async def functions — write async decorators that properly await the wrapped coroutine and detect whether a function is async at decoration time.
A21: Class Decorators
Discover that decorators can be applied to classes as well as functions — modify class attributes, add methods and properties, and compare class decorators with metaclasses and __init_subclass__.
A22: Decorators and Metaclasses
Use metaclasses to automatically apply decorators to all methods of a class, learn how __init_subclass__ offers a simpler alternative, and understand when metaclasses are necessary.
Elective Workshops
A10: Input Validation Decorators
Build decorators that validate function arguments at runtime — using inspect.signature to bind arguments, reading type annotations, and composing reusable constraint factories.
A11: Caching and Memoisation Decorators
Build memoisation decorators from scratch — handling cache keys, keyword arguments, and eviction — then compare with functools.lru_cache and implement time-based cache expiry.
A12: Access Control Decorators
Build permission-checking decorators that gate function access based on user authentication and roles — including @login_required, @requires_role, and method-level access control.
A13: Registration and Plugin Decorators
Build decorators that register functions in central registries — including a command dispatcher, a route system, and an event hook system — exploring the pattern where the decorator records rather than wraps.
A14: Exception Handling Decorators
Build decorators that catch, log, transform, and retry on exceptions — including structured logging, exception mapping, exponential backoff with jitter, and conditional suppression.
A15: Deprecation Decorators
Build configurable @deprecated decorators using the warnings module — covering stacklevel for correct call-site reporting, versioned deprecation, and automatic redirect to replacement functions.
A16: Profiling and Measurement Decorators
Build decorators that measure function execution time, call frequency, and memory usage — using time.perf_counter_ns(), tracemalloc, and cProfile.