Chapter 16: Decorators & Context Managers
Learn how to extend function behavior with decorators and manage resources elegantly with custom context managers.
Downloadchapter16.py
Objectives
- Understand first-class functions and closures.
- Write and apply simple decorators using
@
syntax. - Create parameterized decorators.
- Implement context managers via
__enter__
and__exit__
. - Use
contextlib.contextmanager
for generator-based managers.
1. Closures & Inner Functions
Functions can be passed around and defined inside others:
def make_multiplier(factor):
def multiply(x):
return x * factor
return multiply
times3 = make_multiplier(3)
print(times3(10)) # 30
2. Simple Decorators
Wrap a function to extend its behavior:
def debug(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with", args, kwargs)
result = func(*args, **kwargs)
print(f"{func.__name__} returned", result)
return result
return wrapper
@debug
def add(a, b):
return a + b
add(2, 5)
3. Parameterized Decorators
Decorators that accept their own arguments:
def repeat(n):
def decorator(func):
def wrapper(*args, **kwargs):
for i in range(n):
print(f"Run {i+1}")
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def greet(name):
print("Hello", name)
greet("Alice")
4. Context Managers: __enter__
& __exit__
Create a class that manages a resource:
class Resource:
def __enter__(self):
print("Acquire resource")
return self
def __exit__(self, exc_type, exc, tb):
print("Release resource")
with Resource() as r:
print("Inside with-block")
5. Generator-Based Context Managers
Use @contextmanager
to simplify:
from contextlib import contextmanager
@contextmanager
def open_file(path, mode):
f = open(path, mode)
try:
yield f
finally:
f.close()
with open_file("data.txt", "w") as f:
f.write("Hello")
Exercises
- Write a decorator to time function execution and print the duration.
- Create a decorator that caches a function’s results (memoization).
- Implement a context manager that measures and prints block execution time.
- Rewrite a file-opening class manager using
@contextmanager
.