Functions

Functions in Python are objects — pass them around, wrap them, make them lazy. Five skills that separate tourists from locals.

▶ Watch this reel

What you'll learn

  1. def, args & kwargs
  2. The mutable default-arg trap
  3. lambda & closures
  4. Decorators
  5. Generators & yield

Remember this

def, args, kwargs

def call(model, temperature=0.7, **kwargs): ...
call("gpt-4o", temperature=0.2, stream=True)
tool_args = {"city": "Paris"}; f(**tool_args)   # dict → kwargs

Mutable default trap

def bad(items=[]): ...          # shared list! items persist between calls
def good(items=None):
    items = [] if items is None else items

lambda & closures

Decorators = middleware

def retry(fn):
    @functools.wraps(fn)
    def wrapper(*a, **kw): ...
    return wrapper

Generators & yield

def read_lines(path):
    with open(path) as f:
        for line in f: yield line.strip()

Code: Functions: the five skills in one file

import functools, time, random

# --- 1. args & kwargs: how tool calls arrive -----------------
def call_api(model, temperature=0.7, **kwargs):
    print(f"{model=} {temperature=} extra={kwargs}")

args = {"city": "Paris", "days": 3}          # from an LLM's tool call
def plan_trip(city, days): return f"{days} days in {city}"
plan_trip(**args)                              # dict → keyword args

# --- 2. NEVER do this ----------------------------------------
# def bad(items=[]):  items.append(1); return items   # shared!
def good(items=None):
    items = [] if items is None else items
    items.append(1)
    return items

# --- 3. lambda & closures ------------------------------------
models = ["gpt-4o", "llama-3", "claude-sonnet"]
models.sort(key=lambda m: len(m))

# --- 4. decorators = middleware -------------------------------
def retry(fn):
    @functools.wraps(fn)
    def wrapper(*a, **kw):
        for attempt in range(3):
            try:
                return fn(*a, **kw)
            except Exception:
                if attempt == 2: raise
                time.sleep(0.2 * (attempt + 1))
    return wrapper

@retry
def flaky_llm_call():
    if random.random() < 0.7: raise ConnectionError("429")
    return "ok"

# --- 5. generators = streaming --------------------------------
def read_lines(path):
    with open(path) as f:
        for line in f:
            yield line.strip()

for row in read_lines("bigfile.csv"):   # constant memory
    pass