Functions
Functions in Python are objects — pass them around, wrap them, make them lazy. Five skills that separate tourists from locals.
▶ Watch this reelWhat you'll learn
- def, args & kwargs
- The mutable default-arg trap
- lambda & closures
- Decorators
- Generators & yield
Remember this
- **kwargs collects/splats dicts — how LLM tool arguments reach your code
- Never use mutable defaults; use None + create inside
- Decorators = middleware; yield = lazy streaming with constant memory
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
args→ tuple of extra positionals ·*kwargs→ dict of extra keywords.- LLM tool calls arrive as JSON →
**args. This pattern is everywhere in agent code.
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
lambda m: len(m)— one expression, inline.- Closures capture variables (late binding) — bind loop vars as defaults:
lambda i=i: i.
Decorators = middleware
def retry(fn):
@functools.wraps(fn)
def wrapper(*a, **kw): ...
return wrapper
@decoratorabovedef f≡f = decorator(f).- Use
@functools.wraps(fn)— otherwise traces say 'wrapper'.
Generators & yield
def read_lines(path):
with open(path) as f:
for line in f: yield line.strip()
- Lazy + constant memory; compose into pipelines; sibling: generator expression
(x*x for x in nums).
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