cattrs Structuring — Core Concepts

cattrs is a Python library for converting between unstructured data (dictionaries, lists, primitives) and structured data (attrs classes, dataclasses, typed containers). It complements attrs the way a serialization layer complements a data model: attrs defines the shape, cattrs handles the conversion.

The problem cattrs solves

When data enters a Python application from JSON, YAML, databases, or message queues, it arrives as plain dictionaries and lists. Converting that into typed objects typically means writing manual constructor calls, handling missing keys, coercing types, and dealing with nested structures. This conversion code is tedious, error-prone, and different for every class.

cattrs eliminates this by auto-generating conversion functions from type annotations. You call cattrs.structure(data, TargetType) and get back a fully typed object. You call cattrs.unstructure(obj) and get back a plain dictionary.

How structuring works

When you call structure(data, SomeClass), cattrs:

  1. Inspects SomeClass for its fields and their types (via attrs, dataclasses, or typed dicts).
  2. Generates an optimized structuring function for that class.
  3. Caches the function for reuse.
  4. Applies the function to the input data, recursively structuring nested objects.

For each field, cattrs matches the dictionary key to the field name, then recursively structures the value based on its declared type. A field declared as list[int] causes cattrs to iterate the input list and convert each element to int. A field declared as another attrs class triggers recursive structuring.

Unstructuring: the reverse

unstructure(obj) reverses the process — it converts typed objects back into plain dictionaries. This is useful for JSON serialization, API responses, message publishing, and any context where you need primitive types.

cattrs generates and caches unstructuring functions just like structuring functions. The output is always composed of dicts, lists, strings, numbers, booleans, and None — types that are directly JSON-serializable.

The Converter class

The core of cattrs is the Converter class. The global cattrs.structure and cattrs.unstructure functions use a default converter, but you can create custom converters for different contexts:

A converter for an API might rename fields from snake_case to camelCase. A converter for a database layer might handle custom column types. A converter for a legacy system might apply special date parsing rules.

Each converter maintains its own hook registry and cache, so different parts of your application can have different conversion rules without interfering.

Handling unions and optional types

cattrs handles Optional[X] (which is X | None) automatically — if the value is None, it stays None; otherwise it is structured as X.

For union types like int | str, cattrs needs a disambiguation strategy. You register a “union strategy” that inspects the raw value and determines which type to structure it as. cattrs provides built-in strategies for tagged unions (a discriminator field like "type": "circle") and for simple type-based dispatch.

Common misconception

People often confuse cattrs with Pydantic or Marshmallow. The key difference: cattrs does not define data models. It only converts between existing models and raw data. Your models are defined using attrs, dataclasses, or typed dicts — cattrs just handles the serialization. This separation means you can swap serialization strategies without changing your domain classes.

Performance advantage

cattrs generates specialized functions at first use rather than interpreting schemas at runtime. This makes it significantly faster than generic approaches. In benchmarks, cattrs structures 100,000 flat objects in roughly 200-300ms, comparable to Pydantic v2’s Rust-backed parsing for equivalent workloads.

One thing to remember: cattrs is a pure conversion layer — it generates fast, type-aware converters that translate between raw data and your existing Python classes without coupling serialization logic to your models.

pythoncattrsserializationattrs

See Also