fix: make Number hashable so it works in sets and dict keys#20
Merged
Conversation
Defining __eq__ without __hash__ silently sets __hash__ = None on the
class — that left Number unusable as a set member, dict key, or
lru_cache argument. The new __hash__ uses the canonical fixed-point
string at the configured precision so it agrees with __eq__: two
Numbers that compare equal share that formatted form.
Regression test in tests/test_number_hashable.py exercises set
deduplication, dict-key lookup, and the a == b → hash(a) == hash(b)
contract for equivalent forms like Number("1") and Number("1.0").
See ai/improvements_2026-05-09.md item #6.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem. Python silently sets
__hash__ = Noneon any class that overrides__eq__without defining__hash__.Numberhad__eq__but no__hash__, so{Number("1"), Number("2")}raisedTypeError: unhashable type: 'Number'— andNumbercouldn't be a dict key or anlru_cacheargument either.Fix.
src/formula/formula.py— add__hash__that hashes the canonical fixed-point string at the configured precision. TwoNumberinstances that compare equal via__eq__share that canonical form, satisfying Python'sa == b ⇒ hash(a) == hash(b)contract.Test. New file
tests/test_number_hashable.pycovers set deduplication, dict-key lookup, and the hash/eq agreement for equivalent forms likeNumber("1")andNumber("1.0"). All pass; full suite 320/320.