Generators, Decorators and Context Managers
Harry
· 14 Sep 2026
· 3 views
Advertisement
Generators
Generators yield values one at a time, which keeps memory use tiny for large sequences.
def countdown(n):
while n > 0:
yield n
n -= 1
for x in countdown(3):
print(x) # 3 2 1
squares = (n * n for n in range(5)) # generator expression
print(sum(squares)) # 30Decorators
A decorator wraps a function to add behaviour without changing its code.
def timed(func):
import time
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.time() - start:.4f}s")
return result
return wrapper
@timed
def slow_add(a, b):
return a + b
print(slow_add(2, 3))Context Managers (with)
with open("data.txt", "r") as f:
content = f.read()
# file closed automatically, even on errors
class Managed:
def __enter__(self):
print("opening")
return self
def __exit__(self, *exc):
print("closing")
return False
with Managed():
print("inside block")Closures
def make_multiplier(n):
def multiplier(x):
return x * n # captures n from the outer scope
return multiplier
double = make_multiplier(2)
print(double(10)) # 20Key Points
yieldturns a function into a lazy generator.- Decorators (
@name) wrap and extend functions. withguarantees cleanup of resources.