# Python Decorators: A Primer for Reading AI Agent Code (2026)

> A Python decorators primer for reading AI agent code: what the @ symbol does, how @tool and @app.get work, and why frameworks hand you decorators to apply.

*Source: https://www.infowok.com/python-decorators-primer/ · Sukhveer Kaur · Published June 22, 2026 · Updated June 23, 2026*

---

The first `@tool` line in an agent tutorial stops a lot of beginners cold. There's an `@`, a word, and then a normal-looking function — and somehow that function is now a tool the model can call. This **Python decorators** primer explains that one symbol, so the `@` lines scattered through agent and web code read as ordinary Python instead of syntax you skip past.

You will apply decorators constantly in agent code and write your own rarely, if ever. So the goal here is recognition: know what `@something` does to the function beneath it, and the framework quickstarts stop hiding a step. It's a smaller idea than it looks.

<Prerequisites>

- You can read a basic Python function — new to that? The [Python for AI agents primer](/ai-agents-from-scratch-python-part-0/) covers functions
- That's the whole list — no prior decorator experience assumed

</Prerequisites>

<KeyTakeaways>

- **A decorator wraps a function to add behaviour,** applied with an `@` line directly above the definition.
- **The `@` is pure shorthand:** `@log` above `greet` means exactly `greet = log(greet)`.
- **In agent code you mostly apply, not author, decorators** — `@tool`, `@agent.tool`, `@app.get`.
- **Same syntax, different registrar:** `@tool` registers a tool; `@app.get` registers a web route.

</KeyTakeaways>

## What a decorator actually is

A **decorator** is a function that takes another function, wraps it with some extra behaviour, and gives you back a function to use in its place. You attach one with an `@` line sitting directly above a definition.

```python
@log
def greet(name):
    return f"Hi, {name}"
```

That `@log` line says: "before you bind the name `greet`, pass this function through `log` first." The function still looks normal; the decorator adds something around it — logging, timing, registration — without you editing the body ([Python glossary](https://docs.python.org/3/glossary.html#term-decorator)).

## The `@` is just shorthand

Here's the demystifying part. The `@` syntax is sugar for one plain assignment. These two snippets are **identical**:

```python
@log
def greet(name):
    return f"Hi, {name}"

# ...is exactly the same as:

def greet(name):
    return f"Hi, {name}"
greet = log(greet)        # pass greet to log, rebind the name to the result
```

Read `@log` as "`greet = log(greet)`" and every decorator stops being mysterious. The decorator receives your function, and whatever it returns becomes the new `greet`. That's the entire mechanism — there is nothing hidden underneath ([PEP 318](https://peps.python.org/pep-0318/)).

<Callout type="key" title="The one-line mental model">
`@thing` above a function means `func = thing(func)`. Whenever a decorator confuses you, rewrite it as that assignment and it becomes obvious what's being passed where.
</Callout>

## What a decorator looks like inside

You'll rarely write one, but seeing the shape once removes the mystery for good. A decorator takes a function and returns a *new* function that calls the original with extra behaviour around it.

```python
import functools

def log(func):
    @functools.wraps(func)            # keeps func's name and docstring
    def wrapper(*args, **kwargs):
        print(f"calling {func.__name__}")
        return func(*args, **kwargs)  # the original still runs
    return wrapper
```

The `*args, **kwargs` just means "accept whatever arguments the original took and pass them straight through." The `@functools.wraps(func)` line is a small courtesy that keeps the original function's name and docstring intact — which matters more than you'd think for agents, since **frameworks read the docstring to describe your tool to the model**.

## Why agent code is full of Python decorators

Now the payoff. In agent code, a decorator is how you *register* a plain function with a framework. The function stays ordinary; the `@` line tells the framework what role it plays.

```python
@tool
def get_weather(city: str) -> str:
    """Return the current weather for a city."""
    return f"It's 31C in {city}."
```

`@tool` doesn't change what `get_weather` does — it registers it as a tool the model is allowed to call, building the schema from the type hints and docstring. You'll see the same pattern everywhere: `@agent.tool` in Pydantic AI, and `@app.get("/")` in FastAPI to run a function when a URL is hit. **Same `@` syntax, different registrar.** Recognise that, and the [tool-calling step](/ai-agents-from-scratch-python-part-2/) and the [FastAPI deploy](/build-agentic-ai-app-python-part-2/) both read cleanly.

<Callout type="tip" title="You apply far more than you write">
For your first dozen agents you'll only ever *use* decorators the framework hands you. Treat `@tool` as a label you stick on a function, not a thing you need to build. Authoring your own is a later, optional skill.
</Callout>

## Quick recap

The whole primer, in five lines:

- **A decorator wraps a function** to add behaviour, applied with an `@` line above it.
- **`@thing` means `func = thing(func)`** — pure shorthand for one assignment.
- **Inside,** a decorator returns a new function that calls the original with extras around it.
- **`@functools.wraps`** preserves the name and docstring frameworks read.
- **In practice** you apply framework decorators (`@tool`, `@app.get`), rarely write your own.

## Frequently Asked Questions

**What is a decorator?** A function that wraps another to add behaviour, applied with an `@` line above the definition (e.g. `@tool` registers a tool).

**What does `@` do?** It's shorthand: `@log` above `greet` equals `greet = log(greet)`.

**Do I need to write my own?** Rarely. You mostly apply the ones a framework gives you.

**`@tool` vs `@app.get`?** Both are decorators that register your function — one with an agent framework, one with a web framework.

## Conclusion

Python decorators are just a function wrapping a function, and the `@` is a tidy way to write `func = thing(func)`. Once you read it that way, the `@tool` and `@app.get` lines that open every agent and API tutorial turn from strange punctuation into a plain label: "register this function, leave its body alone." That's all you need to follow the code — and almost all you'll ever need to write.

**Which `@` line first made you pause — `@tool`, `@app.get`, or something else?** Tell me in the comments.

<NextSteps>

- **Need the basics first?** The [Python for AI agents primer](/ai-agents-from-scratch-python-part-0/) covers functions and syntax.
- **See `@tool` in action:** [tool calling in Python](/ai-agents-from-scratch-python-part-2/) registers real functions for the model.
- **Decorators in a web app:** [FastAPI, Docker & deploy](/build-agentic-ai-app-python-part-2/) uses `@app.get` to expose your agent.

</NextSteps>
