Functions and arguments
Package logic into reusable, testable units — the unit of work in every data pipeline and LLM wrapper.
Why this matters in AI / ML / GenAI
Chunking, embedding, prompt building, scoring, and metric computation are all functions. Once logic lives in a function you can unit-test it, reuse it in a notebook and a FastAPI service, and change one place instead of ten.
Defining and calling
def name(parameters): starts a function. The indented block is its body. return sends a value back; a function with no return returns None.
Parameters vs arguments: parameters are the names in the definition, arguments are the values you pass.
Keep functions small and single-purpose. clean_text, chunk_document, build_prompt — three functions beat one process() that does everything, because you can test each one.
Defaults, keywords, and *args / **kwargs
Default values make optional settings readable: def summarize(text, max_words=50):.
Never use a mutable default (def f(items=[])). The list is created once and shared across calls — a genuinely nasty bug. Use items=None and build inside.
Keyword arguments at the call site self-document: generate(prompt, temperature=0.2, max_tokens=256). In LLM code this matters, because generate(p, 0.2, 256) is unreadable.
*args collects extra positional arguments into a tuple; **kwargs collects extra keyword arguments into a dict. LLM SDK wrappers use **kwargs to pass provider-specific options through.
Scope and returning multiple values
Names created inside a function are local — they disappear when it returns. Reading a global is allowed; rebinding one requires global, which you should almost always avoid.
Return several values as a tuple: return mean, std, then mean, std = stats(values). For more than three, return a dict or a dataclass (covered later) so callers are not guessing positions.
Copy-paste examples
Copy into your own editor, or load one into the compiler below and press Run.
A prompt builder function
Defaults + keyword arguments make the call site readable.
def build_prompt(question, context="", style="concise"):
header = f"Answer in a {style} style."
if context:
return f"{header}\n\nContext:\n{context}\n\nQuestion: {question}"
return f"{header}\n\nQuestion: {question}"
print(build_prompt("What is RAG?"))
print("=====")
print(build_prompt("What is RAG?", context="RAG retrieves then generates.", style="detailed"))The mutable default argument trap
Run this. The first function keeps growing across calls — that is the bug.
def broken(item, bucket=[]):
bucket.append(item)
return bucket
def fixed(item, bucket=None):
if bucket is None:
bucket = []
bucket.append(item)
return bucket
print("broken:", broken("a"), broken("b"), broken("c"))
print("fixed :", fixed("a"), fixed("b"), fixed("c"))Return multiple values and pass through kwargs
**kwargs is how thin LLM wrappers forward provider options.
def score_stats(scores):
mean = sum(scores) / len(scores)
worst = min(scores)
return mean, worst
mean, worst = score_stats([0.91, 0.72, 0.85])
print(f"mean={mean:.3f} worst={worst:.2f}")
def call_model(prompt, **kwargs):
options = {"temperature": 0.2, **kwargs}
return {"prompt": prompt[:20], "options": options}
print(call_model("Explain embeddings", max_tokens=128, temperature=0.0))Write a text chunker
Try it — in-browser Python
Change chunk_size to 30 and watch the number of chunks change.
Output
Python runs in your browser. First run downloads the runtime.
Press Run (or Ctrl+Enter) to execute.
CPython in WebAssembly. Stdlib works. NumPy and pandas load on demand. No input(), no GPU, no network installs.
Takeaways
- Small single-purpose functions are testable and reusable across notebook and service.
- Use keyword arguments for model options; never use a mutable default.
- Return tuples for two values, dicts or dataclasses for more.