decimal: Context.copy() Is Independent but Never Equal (tested)

decimal.Context objects hold a precision, rounding mode, and trap settings. We measured whether .copy() gives a truly independent object (it does) and, separately, whether two Context objects with identical settings compare as equal (they don't) -- a combination that trips up caching or test code that assumes equal settings means ==.

1. copy() does produce an independent object -- mutating the copy doesn't touch the original

from decimal import getcontext

ctx = getcontext()
ctx2 = ctx.copy()
ctx2.prec = 2
print("original prec:", ctx.prec, "| copy prec:", ctx2.prec)
original prec: 28 | copy prec: 2

Measured: Context.copy() behaves correctly here -- it's a real, independent copy, not a reference to the same object. Mutating ctx2 leaves ctx untouched. This part matches what most code assumes.

2. But before any mutation, the original and its copy are NOT equal

from decimal import getcontext

ctx = getcontext()
ctx2 = ctx.copy()
print("prec matches:", ctx.prec == ctx2.prec)
print("rounding matches:", ctx.rounding == ctx2.rounding)
print("traps match:", ctx.traps == ctx2.traps)
print("flags match:", ctx.flags == ctx2.flags)
print("ctx == ctx2:", ctx == ctx2)
prec matches: True
rounding matches: True
traps match: True
flags match: True
ctx == ctx2: False

Measured: every individual setting is identical, yet ctx == ctx2 is False. decimal.Context doesn't define __eq__, so == falls back to identity comparison (the same as is) -- a copy is, by definition, never the same object as its source, so it can never equal it even immediately after copying with nothing changed.

3. The risk: deduplication and equality-based caching silently fail

from decimal import Context

a = Context(prec=10)
b = Context(prec=10)
print("separately built, identical settings, ==:", a == b)

seen = {a, b}
print("set size for two identical-settings Contexts:", len(seen))
print("hash(a) == hash(b):", hash(a) == hash(b))
separately built, identical settings, ==: False
set size for two identical-settings Contexts: 2
hash(a) == hash(b): False

Measured: two Context objects built from scratch with the exact same prec (and every other default setting matching) are still unequal and hash differently. Code that deduplicates contexts in a set/dict, caches a context "for prec=10" and compares against a stored baseline with ==, or asserts context_from_pool == expected_context in a test, will silently treat every pair as distinct no matter how identical their settings are. Compare the individual attributes you care about (prec, rounding, traps) explicitly instead of relying on == or set/dict membership on the Context object itself.

Frequently asked questions

Does decimal.Context.copy() create an independent object?

Yes -- we measured mutating the copy's prec leaving the original Context's prec unchanged. copy() is a safe, real copy, not a reference to the same object.

Why does ctx == ctx.copy() return False even though nothing was changed?

Because decimal.Context doesn't define __eq__, so == falls back to identity comparison. We confirmed every individual setting (prec, rounding, traps, flags) matched between the original and the fresh copy, yet == still returned False since they're two different objects.

Can I use a set or dict to deduplicate decimal.Context objects by their settings?

No -- we measured two separately-constructed Context(prec=10) objects with identical settings hashing differently and both landing in a 2-element set. Context has no custom __hash__ or __eq__, so dedup falls back to object identity, not settings equality.

How do I compare two decimal.Context objects for equivalent settings?

Compare the attributes you care about directly, e.g. a.prec == b.prec and a.rounding == b.rounding and a.traps == b.traps, rather than a == b -- we confirmed == on the Context objects themselves uses identity, not settings, even when every attribute matches.

Is decimal.Context hashable based on its settings?

No -- we measured hash(a) != hash(b) for two Context objects with identical prec and other settings. Context's hash (like its equality) is the default identity-based one inherited from object, not derived from its field values.

Written with AI assistance. Measured on 2026-10-04 with Python 3.13.5's built-in decimal module: created a Context, copied it with .copy(), confirmed the copy mutates independently, then compared the original and the unmutated copy (and two separately-constructed Contexts with identical settings) with ==, in a set, and via hash(). PlainHeaders is our extension; Python's decimal module is built in.