Classes vs. dicts

The same data, two different shapes

Every dict you’ve built so far — an agent config, a tool call, a response payload — could just as easily be represented as a class instance instead. Same data, two different containers:

Try it — edit and run
Try it — edit and run

Both work. Both hold the same three values. The question this section answers isn’t “which one is correct” — it’s which one fits a given situation better, and why.

Where a dict wins

A dict is the right shape when the set of keys is genuinely dynamic — unknown ahead of time, varying between instances, or coming directly from somewhere you don’t control the shape of (a parsed JSON API response, for instance). You’ve already used this constantly: **kwargs collects into a dict specifically because the caller’s keyword arguments aren’t knowable in advance.

Try it — edit and run

A dict also wins for genuinely dynamic access — looking up a value by a key you only have as a variable, not as a name you’d type into code:

field_to_check = "temperature"
print(agent_config.get(field_to_check))
No such attribute lookup is this convenient on a class — you'd need
getattr(agent_config, field_to_check) instead, which exists but is
noticeably less idiomatic for this specific case.

(not run live — illustrating the tradeoff, not a working demo)

Where a class wins

A class is the right shape once the fields are known and fixed — AgentConfig always has exactly name, model, and temperature, never some other set. That fixed shape buys you three concrete things a dict can’t offer:

Typos become real errors instead of silent bugs. A wrong dict key just returns None (or raises KeyError, if you’re not using .get()) with no indication of what went wrong; a wrong attribute name is closer to a TypeError immediately, and — as covered next — a good editor will often flag it before you even run the code.

Try it — edit and run

The dict version’s typo produces None — plausible-looking output that’s actually silently wrong, the kind of bug that surfaces three functions later with no clear trail back to its cause. The class version fails immediately, at the exact line the mistake was made.

Autocomplete and inline documentation work. Because AgentConfig has type-hinted parameters, an editor like VS Code knows agent_config. has exactly name, model, and temperature available, and can show that list as you type — a dict’s keys are just strings, invisible to the editor until runtime.

Behavior lives with the data. A class can bundle methods alongside its fields — a dict is data only, so any related logic has to live in a separate free-floating function that takes the dict as an argument:

Try it — edit and run

The gap neither one closes yet

Notice what AgentConfig does not do: it doesn’t check that name is actually a string, or that temperature is actually a number. Nothing stops this:

Try it — edit and run

This runs without complaint — type hints aren’t enforced at runtime, and a plain class’s __init__ is really just a set of assignments with no validation logic of its own. A dict has exactly the same gap; neither container, on its own, actually checks that the data it’s holding is correct. Closing that gap — getting type hints to become real runtime validation — is where the rest of this lesson is headed.

Check your understanding
1/4

Why is a dict the better fit for **kwargs-style data, like arbitrary keyword arguments collected in a function?

Exercise · Graded

Implement __init__ storing tool_name, output, and success (defaulting to True) as instance attributes, then implement as_dict() to return them as a plain dict with the exact keys shown.