Equality and ordering

dataclasses · Essentials · 8 min

Value objects need sensible equality and ordering to be used in tests, sets and sorted collections.

The generated __eq__ compares two instances as tuples of their fields, in definition order, and only if they are of exactly the same class. Comparing with any other type returns NotImplemented, so Point(1, 2) == (1, 2) is False.

@dataclass(order=True) additionally generates __lt__, __le__, __gt__ and __ge__, again comparing field tuples: the first field decides, ties fall through to the next field, like sorting tuples. That makes instances usable with sorted(), min(), max() and heapq.

Because comparison follows field order, declare fields in the order you want to sort by, or exclude fields from comparison with field(compare=False) (for example, a cache or a description). A common trick for priority queues is a first field sort_index set in __post_init__.

Notes:

  • Ordering between different dataclass types raises TypeError.
  • eq=False keeps identity-based equality from object, useful for entities whose identity matters more than their values.
  • If you define __lt__ yourself and pass order=True, you get a TypeError; define your own or let the decorator do it, not both.
  • For ordering by one key only, functools.total_ordering or a key= function in sorted may be clearer.

Example

from dataclasses import dataclass, field

@dataclass(order=True)
class Version:
    major: int
    minor: int
    patch: int = 0
    label: str = field(default="", compare=False)   # ignored in == and <

versions = [Version(1, 10), Version(1, 2, 5), Version(0, 9, label="beta")]
print(sorted(versions))
# [Version(major=0, minor=9, patch=0, label='beta'), Version(major=1, minor=2, ...), Version(major=1, minor=10, ...)]
print(Version(2, 0) > Version(1, 99))             # True
print(Version(1, 0, label="a") == Version(1, 0))  # True: label not compared
print(Version(1, 0) == (1, 0, 0))                 # False: different type

Remember

  • __eq__ compares field tuples of the same class
  • order=True adds <, <=, >, >= using field order
  • field(compare=False) excludes a field from comparisons

Watch out for

  • Declaring fields in an order that doesn't match how instances should sort.

Check yourself

  1. With order=True and fields (major, minor), how is Version(1, 10) compared with Version(1, 2)?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/dataclasses.html#dataclasses.dataclass