Generators, Decorators and Context Managers

Harry · 14 Sep 2026 · 3 views
Advertisement
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))                   # 30

Decorators

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))        # 20

Key Points

  • yield turns a function into a lazy generator.
  • Decorators (@name) wrap and extend functions.
  • with guarantees cleanup of resources.
Share this post:

Comments (0)

Please login or register to comment.