ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 57

  1. Blog

dataclasses.replace vs Manual Copy: Localhost Lab

Hands-on dataclasses.replace vs manual copy/new-instance lab: real ops/s updating one field, measured on Linux localhost in this hands-on lab for SREs.

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 — one-field status flip (p50)
  5. Two-field & hot single
  6. Why replace anyway
  7. Reading it
  8. Frozen note
  9. Field-count sensitivity
  10. Event / reducer style
  11. Pitfalls
  12. Reproduce
  13. Limits
  14. Takeaway

Intro — what this post promises

Update one field on many dataclass instances via dataclasses.replace vs manual new-instance construction vs copy.copy + mutate. This lab reports ops/s on Linux localhost.

It is not dataclass-vs-slots creation/memory (lab 44). Focus: the replace / update pattern.

Related links:

  • dataclass vs slots vs dict localhost lab
  • heapq merge vs sorted localhost lab
  • islice vs list slice localhost lab
  • configparser vs json localhost lab
  • html escape vs manual localhost lab
  • zlib vs gzip compress localhost lab
  • difflib vs set ops localhost lab
  • stat vs path stat localhost lab

Lab honesty (1 Oct 2026 IST): Python 3.13.5. Affiliates: 0. No Docker. Order with 5 fields; also @dataclass(slots=True).

Verdict up front (5000 instances, flip status): manual positional ~6456211 ops/s; manual keyword ~3129734; replace ~900026; copy.copy+mutate ~526727. Prefer replace for clarity when fields evolve; manual wins raw speed if the shape is frozen.


Arms

ArmPattern
replace(o, status=…)stdlib update
Order(…, "paid", …)manual positional
Order(order_id=…)manual keywords
copy.copy then mutateshallow copy
slots variantssame on slotted class
two-field updatereplace vs manual

Lab topology

n=5000 Order objects · 7 rounds · p50
metric: ops/s = n / p50_s (batch) · hot single also timed

Script: lab-evidence/110-dataclass-replace-vs-manual/results/run_lab.py.


Lead table — one-field status flip (p50)

Armops/s
manual positional6456211
manual keyword3129734
manual slots3412857
replace (dict dataclass)900026
replace (slots)527405
copy.copy + mutate526727

Two-field & hot single

Armops/s
manual two fields6107668
replace two fields704108
manual hot single7033260
replace hot single956263

Why replace anyway

replace survives field reorders and defaults — you name only what changes. Manual constructors break silently when someone inserts a field. On this box replace is ~7.2× slower than positional manual for a one-field flip; that tax is often fine outside tight inner loops.


Reading it

  • Domain models / event sourcing copies → replace.
  • Ultra-hot numeric kernels with a stable schema → manual construct may win.
  • copy.copy + mutate trailed here and is easy to get wrong with nested mutables.
  • Slots change allocation; replace still pays helper overhead (lab 44 for create/memory).

Frozen note

With frozen=True, in-place mutation is impossible — replace (or a new construct) is mandatory. The speed story then becomes replace vs manual new, not copy-mutate. Measure that variant if your models are frozen.


Field-count sensitivity

replace inspects the dataclass fields and builds a new instance via the generated constructor. More fields ⇒ more work per call, while a positional manual build always passes every argument explicitly. If your record grows to dozens of columns, re-benchmark — the maintainability case for replace gets stronger even if the relative gap widens.


Event / reducer style

Pipelines that emit “old row → new row” copies map cleanly to replace(old, **changes). That keeps call sites short when only 1–2 keys change across many event types. Micro-optimizing to manual constructors tends to fork a family of nearly identical Order(...) lines that drift.


Pitfalls

  • Mutating shared nested objects after shallow copy.
  • Hand-rolling constructors that drift from the dataclass definition.
  • Using replace in a micro-hot loop without measuring.
  • Confusing this with slots-vs-dict creation benches.

Reproduce

python3 lab-evidence/110-dataclass-replace-vs-manual/results/run_lab.py

Evidence: summary.json, summary.txt.


Limits

One Linux box. Flat string/int fields only. Not frozen=True hash maps. Not pydantic/attrs.


Takeaway

Updating status on 5000 orders: manual ~6456211 ops/s beat replace ~900026 ops/s. Default to dataclasses.replace for maintainability; drop to manual only when profiles prove the gap matters.

dataclasses.replacedataclass updatecopy.copypython dataclasslocalhost labsreops/s

Lab evidence

What I found running this

Lab 1 Oct 2026 IST. Python 3.13.5. n=5000: manual_positional 6456211 ops/s; replace 900026; copy_mutate 526727. Not lab 44 create/slots. Affiliates: 0. Evidence: lab-evidence/110-dataclass-replace-vs-manual/.

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

  • 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

On this page

  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table — one-field status flip (p50)
  5. Two-field & hot single
  6. Why replace anyway
  7. Reading it
  8. Frozen note
  9. Field-count sensitivity
  10. Event / reducer style
  11. Pitfalls
  12. Reproduce
  13. Limits
  14. Takeaway
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove