ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
Topics
sre111localhost lab102performance70python58javascript47bundle size36benchmarking35node.js35web development21typescript19benchmark16content creation15All topics
About
Loading article…

ShopperCove

Hands-on notes on developer tools, written after the tool has actually been run.

All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove

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
  15. FAQ
  16. What does dataclasses.replace do?
  17. Is dataclasses.replace slow?
  18. Do I need replace with frozen dataclasses?
  1. Blog

Python dataclass replace vs Manual Copy: Which Is Faster?

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.

1 October 2026·5 min read

Summary

Disclosure

This post contains affiliate links. If you buy through them, I may earn a commission at no extra cost to you. I only make firsthand claims about products I have actually used.

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
  15. FAQ
  16. What does dataclasses.replace do?
  17. Is dataclasses.replace slow?
  18. Do I need replace with frozen dataclasses?

Intro — what this post promises

Python's dataclasses.replace(obj, field=value) is the safe way to copy a dataclass with one field changed, but it is slower than building the new instance by hand: updating one field on 5,000 instances it ran about 900k ops/s, against about 6.5M for a manual positional constructor (about 7.2x) and 3.1M for keyword arguments. Use replace by default, because it survives field reorders and new defaults, and switch to a manual constructor only where profiling shows a hot loop paying for it.

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

Related links:

  • 01shoppercove.comdataclass vs slots vs dict localhost lab
  • 02shoppercove.comheapq merge vs sorted localhost lab
  • 03shoppercove.comislice vs list slice localhost lab
  • 04shoppercove.comconfigparser vs json localhost lab
  • 05shoppercove.comhtml escape vs manual localhost lab
  • 06shoppercove.comzlib vs gzip compress localhost lab
  • 07shoppercove.comdifflib vs set ops localhost lab
  • 08shoppercove.comstat 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.

FAQ

What does dataclasses.replace do?

It builds a new instance of the dataclass with the fields you name changed and everything else copied, using the generated constructor. You only name what changes.

Is dataclasses.replace slow?

Compared with a manual constructor, yes: about 900k ops/s vs 6.5M positional for a one-field flip on Python 3.13.5. It still beat copy.copy plus mutate (about 527k ops/s).

Do I need replace with frozen dataclasses?

Yes, or a new manual construct. With frozen=True you cannot mutate in place, so replace is the standard way to get an updated copy.

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

Explore the product

Untested by us. Check what's included, the license and refund terms before you buy.

Ready Client Sites for prebuilt websites

See Ready Client Sites details

Notes when a lab post goes up

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

Explore the product

Untested by us. Check what's included, the license and refund terms before you buy.

Ready Client Sites for prebuilt websites

See Ready Client Sites details

Keep reading

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

    Summary1 Oct 20264 min read

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

    Summary1 Oct 20263 min read

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

    Summary1 Oct 20263 min read