python
52 lines · 7 steps
Copy-on-write attribute snapshots in Python
A wrapper that layers edits over an untouched base object, deferring real mutation until you commit.
Explained by
highlit
1from copy import deepcopy
2
3
4class CowSnapshot:
5 __slots__ = ("_base", "_overrides", "_deleted")
6
7 def __init__(self, base):
8 object.__setattr__(self, "_base", base)
9 object.__setattr__(self, "_overrides", {})
10 object.__setattr__(self, "_deleted", set())
11
12 def __getattr__(self, name):
13 overrides = object.__getattribute__(self, "_overrides")
14 if name in overrides:
15 return overrides[name]
16 if name in object.__getattribute__(self, "_deleted"):
17 raise AttributeError(name)
18 return getattr(object.__getattribute__(self, "_base"), name)
19
20 def __setattr__(self, name, value):
21 object.__getattribute__(self, "_deleted").discard(name)
22 object.__getattribute__(self, "_overrides")[name] = value
23
24 def __delattr__(self, name):
25 overrides = object.__getattribute__(self, "_overrides")
26 overrides.pop(name, None)
27 object.__getattribute__(self, "_deleted").add(name)
28
29 def is_dirty(self):
30 return bool(object.__getattribute__(self, "_overrides")) or bool(
31 object.__getattribute__(self, "_deleted")
32 )
33
34 def commit(self):
35 base = object.__getattribute__(self, "_base")
36 for name, value in object.__getattribute__(self, "_overrides").items():
37 setattr(base, name, value)
38 for name in object.__getattribute__(self, "_deleted"):
39 if hasattr(base, name):
40 delattr(base, name)
41 object.__getattribute__(self, "_overrides").clear()
42 object.__getattribute__(self, "_deleted").clear()
43 return base
44
45 def materialize(self):
46 snapshot = deepcopy(object.__getattribute__(self, "_base"))
47 for name, value in object.__getattribute__(self, "_overrides").items():
48 setattr(snapshot, name, deepcopy(value))
49 for name in object.__getattribute__(self, "_deleted"):
50 if hasattr(snapshot, name):
51 delattr(snapshot, name)
52 return snapshot
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Overriding the attribute dunders lets you intercept every read, write, and delete to build a transparent proxy.
- 2A tombstone set distinguishes 'not set here' from 'explicitly deleted', which a plain overrides dict can't express alone.
- 3Deferring mutation to an explicit commit gives you cheap, discardable edits over shared state.
Related explainers
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
python
import random from typing import Iterator, List
How reservoir sampling picks k items
reservoir-sampling
streaming
randomness
Intermediate
5 steps
go
package logging import ( "context"
Deduplicating log attributes in Go's slog
decorator-pattern
structured-logging
immutability
Intermediate
8 steps
python
import secrets from django.contrib.auth import authenticate, login from django.core.cache import cache
Two-factor login with OTP in Django
two-factor-auth
one-time-passwords
caching
Intermediate
9 steps
python
import re from functools import total_ordering from typing import Optional
Parsing and comparing semantic versions
regex
operator-overloading
sorting
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/copy-on-write-attribute-snapshots-in-python-explained-python-32b3/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.