JJoeven

Curriculum/Python

Functions

Define a function with def, return a value, write a docstring, pass keyword arguments, and store tools in a dict.

beginner20 min16 / 37

A function is a block of code with a name. You define it once. You call it when you need that work done. The definition is the recipe. The call is cooking one meal. You can call the same function many times with different inputs.

In an agent, a tool is a function. A tool is an action the program can run, such as search or add. The model picks a tool name and inputs. Your Python looks up the function and calls it. If you can write a small function that returns a value, you can write a tool.

In, then out
q, ksearchhits

A function takes inputs and returns a value. print only shows text. return is for other code.

In, then out

Functions also keep you from copying the same five lines into every branch. Name the work. Call the name. Test the name.

What you will learn

  • def starts a function
  • return sends a value back; print only shows text
  • A docstring is a note inside the function
  • Keyword arguments and in a call**
  • A dict of functions, plus call_tool(name, **kwargs)

The parts of a function

WordMeaningExample
NameWhat you callsearch
ParametersInput names in the definitionq, k
ArgumentsValues you pass in a call"rain", 2
ReturnThe value sent backa list of hits
BodyThe indented linesthe work

The name should be a verb or a job: search, add, call_tool. data is a bad function name. snake_case, like variables.

def and return

def means “define this function.” Names in parentheses are parameters (the inputs). The body is indented. return hands a value back and stops the function. Lines after return in that path do not run.

python
def search(q, k=3):
    hits = []
    for i in range(k):
        hits.append(class="tok-s">"doc " + str(i) + class="tok-s">" about " + q)
    return hits

print(search(class="tok-s">"weather"))
print(search(class="tok-s">"weather", 1))
  • No return means the function returns None. None means “no value.”
  • search("weather") is a positional call: values line up in order. First value fills first parameter.
  • search(q="weather", k=1) is a keyword call: you pass inputs by name. Order can change: search(k=1, q="weather") is the same.

After you use a keyword, the rest of that call must be keywords too. search(q="weather", 1) is a syntax error. search("weather", k=1) is fine: positional first, then keyword.

Defaults like k=3 mean the caller may skip that input. Defaults have a trap when the default is a list. The next lesson covers that. Numbers and strings as defaults are safe.

Return vs print

print is for people. It writes text on the screen. return is for other code. It sends a value back to the caller. You can do both. Beginners often print and forget to return. Then hits = search("rain") stores None.

A tool must return data. Then the agent can save that data in a transcript (a list of what happened). You may also print while you debug. The useful result is still the return value.

python
def add(a, b):
    print(class="tok-s">"adding")  class="tok-c"># for you to read
    return a + b     class="tok-c"># for the program to use

total = add(2, 3)
print(class="tok-s">"got", total)

If you only print inside add and do not return, total is None, and print("got", total) shows None. The addition happened. Nobody handed it back.

return can send any value: number, string, list, dict, tuple, None. Tools often return a dict: {"ok": True, "result": ...}. That shape is easy to store and easy to test.

A note inside the function

The first string in a function body is a docstring. A docstring is a note inside the function. It tells a person (or a model) what the function does. It does not change the result. Triple quotes let the note span lines.

python
def search(q, k=3):
    class="tok-s">"""Return k fake documents for query q."""
    return [class="tok-s">"doc 0 about " + q]

Keep the note short and clear. Tool descriptions you send to a model are the same idea: they say how to call the function. A docstring that lies is worse than none. Update it when you change the return shape.

You can read it with search.__doc__. You do not need that often. Editors show it. Tests sometimes check it. Humans read it first.

pass means "do nothing yet"

Python needs at least one line in a block. pass is that line when you have not written the real body yet.

python
def search(q):
    pass  class="tok-c"># fill this in later

If you leave the body empty, Python raises an error. pass is the empty body. Use it while you design a tool. Replace it before you ship. A function that only passes returns None. That can look like “the tool ran” if you forget to fill it in.

Keyword arguments and unpacking a dict

Models often send inputs as a dict, like {"q": "agents", "k": 2}.

In a call, two stars in front of a dict unpack it. Unpack means “turn this dict into named inputs.”

python
args = {class="tok-s">"q": class="tok-s">"agents", class="tok-s">"k": 2}
print(search(**args))   class="tok-c"># same as search(q="agents", k=2)

That ** is in the call. If the dict has a key the function does not accept, Python raises TypeError. Check keys, or write a function that accepts extras (next lesson). If a required name is missing, you also get TypeError.

A dict of functions

Functions are values. You can store them in a dict without calling them. search is the function. search("q") is a call. The dict stores the function.

That dict is a tool registry: a map from a name to a function.

python
tools = {class="tok-s">"search": search, class="tok-s">"add": add}
print(tools[class="tok-s">"search"](class="tok-s">"python"))

A helper like call_tool(name, **kwargs) looks up the name, then calls the function with the named inputs. If the name is missing, return an error dict. Do not crash the agent loop.

Common mistakes

  • Printing instead of returning.
  • memory = memory.append(x) inside a function, same None trap.
  • Calling tools["search"] without () when you meant to run it — you get the function object.
  • **args with extra or missing keys.
  • Forgetting parentheses on def search(): or the colon.
Live PythonOpen full playgroundpython
Output
Run to execute this in your browser. Nothing is sent to a server.

Change "shell" to "add" and pass a and b. Click Run. Unknown names should stay error dicts, not crashes.

If a Try it box looks empty, you forgot print. The function returned a value and nobody showed it.

How agents use this

When a model asks for a tool, it sends a name and a dict of inputs. Your job is registry[name](**inputs). That is the whole trick. Keep each tool small. Give it clear inputs. Return a dict, list, string, or number. Huge functions with hidden names are hard to test and hard to read in a log.

call_tool is the executor. The model never calls Python directly. It only emits a name. Your registry is the allowlist and the lookup table in one. If a name is not in the dict, it is not a tool. There is no second back door.

Docstrings (or a separate description string) are what you later send the model so it knows the arguments. The function body is what actually runs. Keep those two aligned. A tool that claims it searches the web but returns a constant string will pass a demo and fail in production. Return real data. Print only for you.

Check your understanding

What does return do that print does not?