Skip to content

Equality, Ordering, and Hashing as Container Contracts

Page Maps

graph LR
  family["Python Programming"]
  program["Python Object-Oriented Programming"]
  section["Object Semantics Data Model"]
  page["Equality, Ordering, and Hashing as Container Contracts"]
  capstone["Capstone evidence"]

  family --> program --> section --> page
  page -.applies in.-> capstone
flowchart LR
  orient["Orient on the page map"] --> read["Read the main claim and examples"]
  read --> inspect["Inspect the related code, proof, or capstone surface"]
  inspect --> verify["Run or review the verification path"]
  verify --> apply["Apply the idea back to the module and capstone"]

Read the first diagram as a placement map: this page is one concept inside its parent module, not a detached essay, and the capstone is the pressure test for whether the idea holds. Read the second diagram as the working rhythm for the page: name the problem, study the example, identify the boundary, then carry one review question forward.

Why this lesson matters

Equality and hashing stop being "just operator behavior" the moment an object enters a set, becomes a dictionary key, or participates in deduplication logic.

This lesson exists because many bugs come from half-made decisions:

  • value equality is implemented, but hashing is forgotten
  • ordering is added because sorting seems convenient, not because the type has a real order
  • mutable objects are treated as stable keys
  • identity-based and value-based reasoning are mixed without being named

Start with the real question

Before writing __eq__, ask:

When should two instances count as the same for the purposes of this program?

That answer should come from meaning, not from convenience.

Identity equality versus value equality

Identity-oriented equality

This is right when continuity matters more than current field values.

Examples:

  • active sessions
  • tracked alerts
  • workflow objects with lifecycle

In these cases, two objects that look similar may still be different things.

Value-oriented equality

This is right when content is the meaning.

Examples:

  • coordinates
  • thresholds
  • units or names
  • immutable configuration fragments

In these cases, equal content usually means interchangeable values.

The hashing rule that must stay true

If two objects compare equal and are hashable, they must produce the same hash.

That is the contract container code is relying on.

You do not need a mathematical lecture here. You need one durable rule:

  • if equality changes, hashing must be reviewed too

And one strong warning:

  • if equality depends on mutable state, hashing is usually a trap

Ordering is optional, not a badge of completeness

Do not add ordering because the type "might be sortable."

Add ordering only when the type has a natural and reviewable order.

Good candidates:

  • timestamps
  • semantic version pieces with clear rules
  • value types where less-than has obvious meaning

Bad candidates:

  • rich entities whose lifecycle matters more than field comparison
  • objects where several orderings are plausible
  • domain objects where sorting is only a UI convenience

If a type has no obvious single order, use an explicit sort key at the call site.

A small example with good value semantics

from dataclasses import dataclass


@dataclass(frozen=True)
class Threshold:
    metric_name: str
    limit: int

This is value-oriented:

  • two thresholds with the same fields mean the same thing
  • hashability is safe because the fields are frozen
  • set membership or deduplication becomes predictable

A small example where identity should stay primary

class Alert:
    def __init__(self, alert_id: str, threshold):
        self.alert_id = alert_id
        self.threshold = threshold
        self.active = False

If Alert is a tracked workflow object, forcing value equality on its fields may be a mistake. Two alerts can look similar and still represent different lifecycles.

That is why this lesson begins with meaning, not with dunder methods.

Review checklist for equality and hashing

Question Good sign Warning sign
What does equality mean? one clear domain story "it depends on context" but nothing is modeled
Should the type be hashable? stable value semantics mutable fields participate in equality
Does ordering exist naturally? a reviewer can explain the less-than meaning ordering added only because sorting felt useful
Will a set or dict rely on this type? semantics are explicit and tested container use is accidental

Common mistakes

Mistake Why it fails
overriding __eq__ without thinking about hashability container behavior becomes unsafe or surprising
keeping hashability on a mutable value-like object the object can drift after insertion into a set or dict
implementing ordering for a type with no natural order code becomes readable only to the original author
using equality to model lifecycle identity distinct entities collapse into one semantic bucket

Practical rules for this course

  • Prefer value equality for small immutable meaning-carrying types.
  • Prefer identity-based behavior for lifecycle-bearing entities unless you have a very strong reason not to.
  • Make mutable equality-bearing types unhashable unless the design story is exceptionally clear.
  • Use explicit sort keys instead of fake total ordering when the type has several plausible orderings.

Capstone connection

This lesson underpins questions like:

  • should rule definitions compare by content?
  • should workflow objects keep identity semantics?
  • can this type safely be deduplicated or used in set membership?

If those answers are fuzzy, the later architecture will inherit the fuzziness.

Exit check

Leave this lesson only when you can do all of these:

  • explain whether one type should compare by identity or by value
  • say whether that type should be hashable and why
  • reject one case where adding ordering would only create false precision