Decorators
🎀 Decorators
functools.wraps (a fix for a subtle side effect), @property for attribute-like method access, and functools.lru_cache for automatic memoization.
🔧 Functions Are Objects
Decorators only make sense once you know functions in Python are ordinary objects — they can be assigned to variables, passed as arguments, and returned from other functions:
🎁 Writing a Basic Decorator
@shout above def greet is syntax sugar for greet = shout(greet) — greet now actually refers to wrapper, which calls the original greet logic internally (as func) and transforms its result before returning it.
If wrapper is defined as def wrapper(): with no parameters, decorating greet(name) breaks entirely — calling greet("Ada") actually calls wrapper("Ada"), which doesn't accept any arguments. Using *args, **kwargs (Course 1, Chapter 8) lets the wrapper forward whatever arguments it received straight through to func, so the decorator works on any function regardless of its parameter list.
🏷️ functools.wraps
Without @wraps(func), the decorated function's identity (its __name__, docstring, and other metadata) silently becomes wrapper's instead of the original function's — confusing for debugging, introspection, and tools that rely on that metadata. @wraps(func) from functools is standard practice in every real decorator.
📐 @property
@property makes a method accessible as if it were a plain attribute — c.area, not c.area() — while still running real code every time it's read. This is useful for a value that's always derived from other attributes (like area from radius) rather than stored directly.
💾 functools.lru_cache
@lru_cache automatically remembers previous return values for previous arguments, so calling fibonacci(30) a second time (or any recursive call the first computation already made) returns the cached result instantly instead of recomputing it. This turns the naive recursive Fibonacci implementation — normally exponentially slow — into something fast, with a single decorator line and no change to the function's actual logic.
Wrapping Behavior: Go vs Kotlin vs Python
| Language | Approach |
|---|---|
| Go | No decorator syntax at all — the same effect is achieved manually with higher-order functions (a function that takes and returns a func) or the "middleware" pattern common in HTTP handlers. |
| Kotlin | No direct decorator syntax either — achieved with higher-order functions and lambdas, or with annotations processed at compile/build time for a different (metadata-driven) purpose. |
| Python | First-class @decorator syntax, built directly into the language. |
Python's @decorator syntax is a genuine convenience neither Go nor Kotlin has natively — both can express the same underlying higher-order-function idea, just without the dedicated syntax sugar.
@decorator
Sugar for func = decorator(func) — wraps a function with extra behavior.
*args, **kwargs
Required in the wrapper so it can forward any function's arguments through.
@property
Makes a method readable like a plain attribute, computed live.
@lru_cache
Automatic memoization — remembers results for previously-seen arguments.
💻 Coding Challenges
Challenge 1: A Timing Decorator
Write a decorator @timer that prints how long the decorated function took to run (use the time module's time.time() before and after calling the function), then returns the function's original result unchanged. Apply it to any simple function.
Goal: Practice writing a decorator that measures something without altering the wrapped function's return value.
Challenge 2: Rectangle with @property
Write a Rectangle class with width and height instance attributes, plus an @property method area that returns width * height. Create an instance and print its .area.
Goal: Practice defining and using a computed @property.
Challenge 3: Cached Fibonacci, Verified
Using @lru_cache, write the recursive fibonacci(n) function from this chapter, then call fibonacci(35) and print the result — something the same function without @lru_cache would take noticeably longer to compute.
Goal: See @lru_cache's real performance impact firsthand, not just read about it.
🎯 What's Next
Next chapter: Working with Data — JSON, CSV, and the datetime module.