dacite review
dacite 1.9.2 turns a dictionary into an instance of a standard Python dataclass. `from_dict()` follows annotations through nested dataclasses, collections, optionals, unions, generics, and forward references. Configuration covers type hooks, constructor casts, key conversion, extra-key rejection, and ambiguous unions. The current release fixes Protocol type declarations and avoids writing to read-only typing internals. Our install was pure Python, included `py.typed`, and imported successfully, but dacite still draws a hard boundary: it constructs objects and does not validate business rules or serialize them.
dacite 1.9.2 installed in 0.2 seconds, occupied 1 MB, imported in 0.14 seconds, and had 0 known vulnerabilities in our sandbox. Install it for trusted dictionary-to-dataclass construction; use a validation or serialization library when the input boundary needs more than annotated type checks.
We installed it
| Install | ✓ · 0.2s | 1 package on disk · 1 MB |
| Import | ✓ | import dacite in 0.14s · pure Python · py.typed · requires Python >=3.7 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does dacite install cleanly?
Yes. In a fresh container with an empty cache, pip install dacite finished in 0.2s, leaving 1 package and 1 MB on disk. pip-audit reported no known vulnerabilities.
What does dacite need to run?
Python >=3.7, and nothing compiled: it is pure Python. In our run import dacite succeeded in 0.14s, and the package ships py.typed for type checkers.
dacite or cattrs: which should you use?
cattrs: Choose it when dataclasses or attrs classes must be converted in both directions. dacite 1.9.2 installed in 0.2 seconds, occupied 1 MB, imported in 0.14 seconds, and had 0 known vulnerabilities in our sandbox.
When should you not use dacite?
The dictionary comes straight from an untrusted request. The README says dacite is not a validation library, so ranges, formats, and cross-field rules need another layer.
Use it if
- Your application already uses standard-library dataclasses and receives trusted dictionaries that need nested object construction.
- A single `from_dict()` call plus a small `Config` is preferable to adding a second model system.
- Type-wide hooks, enum casts, and one field-name conversion function cover the input format.
- You want field-path exceptions for missing values, wrong runtime types, unexpected keys, and union failures.
- The dictionary comes straight from an untrusted request. The README says dacite is not a validation library, so ranges, formats, and cross-field rules need another layer.
- You also need JSON output or JSON Schema. dacite has no unstructuring, dumping, or schema API; cattrs, mashumaro, or Pydantic covers that wider boundary.
- Union members accept the same shape. dacite tries members in annotation order by default, while `strict_unions_match=True` only reports the ambiguity and has no discriminator setting.
- Different fields need different aliases or migrations. `convert_key` is one function applied from every dataclass field name to its input key.
- Your Python support policy starts with a recently maintained release train. Version 1.9.2 shipped in February 2025, and the repository's latest push was March 2025.
Setup reality
We installed dacite 1.9.2 in a fresh Python 3.12 Bookworm sandbox. Installation finished in 0.2 seconds and left 1 package using 1 MB on disk. The measured metadata declared 9 direct dependencies and Python 3.7 or newer. import dacite completed in 0.14 seconds. The wheel is pure Python, ships py.typed, and pip-audit found 0 known vulnerabilities.
The first decision is whether unknown input keys should disappear. They are ignored unless Config(strict=True) is set. Runtime type checking is enabled, but it only compares values with annotations. A negative age, malformed email, or invalid relationship between 2 fields can still reach the dataclass. Validate those rules before construction and translate DaciteFieldError subclasses at the boundary.
Conversions have broad scope. A hook registered for datetime runs for every matching field, and cast=[Enum] calls each enum constructor. convert_key receives the dataclass field name and returns the dictionary key, so a camel-case adapter runs in the opposite direction many developers first assume. Forward references that Python cannot resolve need an explicit forward_references mapping.
Union matching is ordered. With Cat | Dog, dacite accepts the first member whose annotations fit unless strict union matching is enabled. Version 1.9.2 fixed Protocol declarations and read-only typing internals, following the generic and forward-reference work in 1.9.0. Resolved type information is cached process-wide; the cache defaults to 2,048 entries and can be resized or cleared when an application creates dataclass types dynamically.
Patterns
Build a dataclass from a dictionary construct-dataclass
from dataclasses import dataclass
from dacite import from_dict
@dataclass
class User:
name: str
age: int
user = from_dict(User, {'name': 'Ada', 'age': 37})`from_dict()` checks the annotated runtime types, but 1 integer check does not enforce a nonnegative age.
Create nested dataclasses construct-nested-model
from dataclasses import dataclass
from dacite import from_dict
@dataclass
class Address:
city: str
@dataclass
class Customer:
address: Address
customer = from_dict(Customer, {'address': {'city': 'London'}})The `Address` annotation makes dacite turn 1 nested dictionary into an `Address` instance.
Convert a list of records construct-list-items
@dataclass
class Line:
sku: str
@dataclass
class Order:
lines: list[Line]
order = from_dict(Order, {'lines': [{'sku': 'A1'}]})`list[Line]` supplies the item type; a bare `list` gives dacite no dataclass to construct.
Allow a missing optional field handle-optional-field
@dataclass
class Profile:
handle: str
bio: str | None
profile = from_dict(Profile, {'handle': 'ada'})
assert profile.bio is NoneAn omitted optional field becomes `None`; 1 omitted required field without a default raises `MissingValueError`.
Parse timestamps with a type hook parse-datetime
from datetime import datetime
from dacite import Config
config = Config(type_hooks={datetime: datetime.fromisoformat})
event = from_dict(Event, {'occurred_at': '2026-08-22T12:30:00+00:00'}, config)The 1 `datetime` hook runs for every field with that target type, and parsing failures come from the hook.
Construct enum values cast-enum
from enum import Enum
from dacite import Config
class State(Enum):
OPEN = 'open'
CLOSED = 'closed'
ticket = from_dict(Ticket, {'state': 'open'}, Config(cast=[Enum]))Casting the `Enum` base class affects every enum field; an unknown value raises from its constructor.
Reject unknown input keys reject-extra-keys
from dacite import Config, UnexpectedDataError
try:
user = from_dict(User, payload, Config(strict=True))
except UnexpectedDataError as error:
raise ValueError(sorted(error.keys)) from errorStrict mode defaults to false, so 1 misspelled payload key is otherwise ignored.
Require one union match reject-ambiguous-union
config = Config(strict_unions_match=True)
pet = from_dict(Pet, {'animal': {'name': 'Milo'}}, config)If 2 union members accept this shape, strict matching raises instead of choosing by annotation order.
Read camel-case dictionary keys convert-input-keys
def to_camel(field: str) -> str:
first, *rest = field.split('_')
return first + ''.join(part.title() for part in rest)
config = Config(convert_key=to_camel)
person = from_dict(Person, {'firstName': 'Ada'}, config)`convert_key` maps each dataclass field name to 1 source dictionary key, rather than converting incoming keys first.
Resolve a forward-referenced type resolve-forward-reference
config = Config(forward_references={'Leaf': Leaf})
node = from_dict(Node, {'child': {'value': 'x'}}, config)The 1-entry namespace lets Python resolve `Leaf`; an unresolved name raises `ForwardReferenceError`.
Expose the failing field path translate-field-error
from dacite import DaciteFieldError
try:
order = from_dict(Order, payload)
except DaciteFieldError as error:
raise ValueError(error.field_path) from errorField-aware exceptions include a nested path; `UnexpectedDataError` and `ForwardReferenceError` need separate handlers.
Clear cached type resolution manage-type-cache
from dacite import clear_cache, get_cache_size, set_cache_size
assert get_cache_size() == 2048
set_cache_size(4096)
try:
run_dynamic_imports()
finally:
clear_cache()The cache is process-wide and defaults to 2,048 entries; resize it only after measuring dynamic dataclass workloads.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| cattrs | PyPI | Choose it when dataclasses or attrs classes must be converted in both directions. |
| mashumaro | PyPI | Choose it for generated serialization methods and multiple wire formats on dataclasses. |
| pydantic | PyPI | Choose it when parsing, constraints, aliases, errors, schemas, and serialization belong in one model layer. |
| dataclasses-json | PyPI | Choose it when JSON load and dump methods attached to dataclasses fit the project better than a standalone mapper. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

