Plate 09
dataclasses.asdict vs vars: Localhost Lab
Aditya Challa4 min read
Intro — what this post promises
Turn dataclasses into dicts with dataclasses.asdict vs vars() / dict(__dict__) / manual fields. This lab reports ops/s on Linux localhost, plus deep-vs-shallow behavior checks.
Related links:
- xml etree vs json localhost lab
- logging formatter vs fstring localhost lab
- fractions vs float localhost lab
- math fsum vs sum localhost lab
- sqlite3 vs shelve localhost lab
- gc collect cost localhost lab
- copy copy vs dict copy localhost lab
- html parser vs regex localhost lab
Lab honesty (1 Oct 2026 IST): Python 3.13.5. Affiliates: 0. Differentiates from dataclass-replace (lab 110) and slots (lab 43) — focus is asdict deepcopy semantics vs cheap vars.
Verdict up front (n=50000): vars flat ~26244997 ops/s; asdict flat ~712136; manual ~12349533; nested asdict ~502824 vs vars ~26083074.
Arms
| Arm | Pattern |
|---|---|
asdict(flat) | recursive deepcopy-ish |
vars(flat) | shallow attribute dict |
dict(o.__dict__) | shallow copy of mapping |
| manual field dict | explicit keys |
asdict(nested) | nested Event → dict |
vars(nested) | Event object still inside |
Seven rounds, p50.
Lab topology
Script: lab-evidence/130-dataclass-asdict-vs-vars/results/run_lab.py.
Lead table (p50 ops/s)
| Arm | ops/s |
|---|---|
| vars flat | 26244997 |
| dict(dict) flat | 9262266 |
| manual flat | 12349533 |
| asdict flat | 712136 |
| vars nested | 26083074 |
| asdict nested | 502824 |
vars led by roughly 36.9× on the flat arm — because it does not deep-copy.
Semantics (why the gap exists)
asdict(nested)["event"]is adict;vars(nested)["event"]is still anEvent.asdicttags list is a new list (asdict_new_list=True);varsshares the same list (vars_same_list=True).
If you JSON-dump for a wire format, you usually want asdict (or a dedicated serializer). If you only need a shallow attribute bag for logging filters, vars is enough — and far cheaper.
Reading it for SRE work
- Export/metrics payload →
asdict(or manual +json.dumps) so nested dataclasses become JSON-safe. - Hot path “mirror attributes” →
vars/__dict__copy; never mutate shared lists casually. - Lab 110 covers
replace; this post is dict projection cost. - Slots instances without
__dict__breakvars— know your type.
Document which projection your exporters use so on-call does not “optimize” asdict into vars and ship non-JSON Event objects.
Nested cost
Nested asdict fell to ~502824 ops/s while nested vars stayed near ~26083074. Depth multiplies asdict work; vars stays O(fields on the outer object).
Manual vs asdict
A hand-built field dict hit ~12349533 ops/s — much closer to vars than to asdict, because it still shares the tags list reference unless you copy it yourself. Use manual dicts when you want a stable, shallow export schema without recursive deepcopy. Reach for asdict when nested dataclasses must become plain dict/list trees for JSON or msgpack.
If exporters disagree (one uses vars, one uses asdict), you will debug “sometimes tags mutate upstream” pages — lock one helper in the shared library.
Pitfalls
- Treating
varsoutput as a deep snapshot. - Mutating
vars(obj)["tags"]and surprising the dataclass. - Using
asdictin a per-request hot path without measuring. - Forgetting slots /
__dict__-less objects.
Reproduce
Evidence: summary.json, summary.txt.
Limits
One Linux box. Stdlib dataclasses only. Not pydantic/msgspec.
Takeaway
vars ~26244997 ops/s vs asdict ~712136 on flat events — pick asdict when you need nested dicts and copied containers; pick vars when a shallow view is enough.
Lab evidence
What I found running this
Lab 1 Oct 2026 IST. Python 3.13.5. n=50000: vars 26244997 ops/s; asdict 712136; nested asdict 502824. asdict deep-copies; vars shallow. Affiliates: 0. Evidence: lab-evidence/130-dataclass-asdict-vs-vars/.
Related links
Plate 17
platform vs os.uname Inventory: Localhost Lab
Hands-on platform.platform vs os.uname host inventory lab: real ops/s plus cache notes, measured on Linux localhost today in this hands-on lab for SREs.
1 Oct 2026
Plate 76
cmath vs math.hypot Magnitudes: Localhost Lab
Hands-on cmath vs math.hypot magnitude ops lab: real ops/s for abs, polar, and phase, measured on Linux localhost today in this hands-on lab for SREs.
1 Oct 2026
Plate 75
uuid.uuid4 vs uuid.uuid1: Localhost Lab
Hands-on uuid.uuid4 vs uuid.uuid1 ID generation lab: real ops/s plus version/node checks, measured on Linux localhost today in this hands-on lab for SREs.
1 Oct 2026