ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 09

  1. Blog

dataclasses.asdict vs vars: Localhost Lab

Aditya Challa·1 October 2026·4 min read

Summary
On this page
  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table (p50 ops/s)
  5. Semantics (why the gap exists)
  6. Reading it for SRE work
  7. Nested cost
  8. Manual vs asdict
  9. Pitfalls
  10. Reproduce
  11. Limits
  12. Takeaway

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

ArmPattern
asdict(flat)recursive deepcopy-ish
vars(flat)shallow attribute dict
dict(o.__dict__)shallow copy of mapping
manual field dictexplicit keys
asdict(nested)nested Event → dict
vars(nested)Event object still inside

Seven rounds, p50.


Lab topology

n=50000 · flat Event + Nested(Event) · 7 rounds · p50
metric: ops/s = n / p50_s

Script: lab-evidence/130-dataclass-asdict-vs-vars/results/run_lab.py.


Lead table (p50 ops/s)

Armops/s
vars flat26244997
dict(dict) flat9262266
manual flat12349533
asdict flat712136
vars nested26083074
asdict nested502824

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 a dict; vars(nested)["event"] is still an Event.
  • asdict tags list is a new list (asdict_new_list=True); vars shares 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__ break vars — 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 vars output as a deep snapshot.
  • Mutating vars(obj)["tags"] and surprising the dataclass.
  • Using asdict in a per-request hot path without measuring.
  • Forgetting slots / __dict__-less objects.

Reproduce

python3 lab-evidence/130-dataclass-asdict-vs-vars/results/run_lab.py

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.

dataclasses.asdictvarsdataclasspythonlocalhost labsreops/s

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/.

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

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

On this page

  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table (p50 ops/s)
  5. Semantics (why the gap exists)
  6. Reading it for SRE work
  7. Nested cost
  8. Manual vs asdict
  9. Pitfalls
  10. Reproduce
  11. Limits
  12. Takeaway
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove