#๐Ÿ”’ `dataclass` order=True comparators use deep comparisons. Can this be changed?

11 messages ยท Page 1 of 1 (latest)

fossil granite
#

With @dataclasses with the order=True , a class has its dunder comparator functions (__lt__, __gt__, etc) generated. The comparators generate code such that the class is compared as if it were a tuple. This means it's a deep comparison of every field.

For example, given:

@dataclass(order=True)
class Widget:
    id: int
    x: int
    y: int

# the generated __eq__ dunder is similar to this
def ExampleEq(widget_a, widget_b):
    if widget_a.id < widget_b.id:
        return -1
    elif widget_a.id > widget_b.id:
        return 1
    if widget_a.x < widget_b.x:
        return -1
    elif widget_a.x > widget_b.x:
        return 1
    if widget_a.y < widget_b.y:
        return -1
    elif widget_a.y > widget_b.y:
        return 1

    return 0

# pseudocode usage
w = Widget(id=0, x=1, y=1)
ExampleEq(w, w)

Ideally, if 2 Widgets have the same id, I don't want to superfluously check the x and y fields.

Is there a way to customize the generated dunder methods with dataclass so the comparison is only done on a single field? Can that field be specified and not simply be the first field in the class? Is there an alternative option to the builtin dataclass?

errant totemBOT
#

@fossil granite

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

patent spire
#

I assume you'll have to write your own __lt__, and then use functools.total_ordering to generate the other dunders

#

it's easy

#

here's an example I have lying around ```py
@functools.total_ordering
@dataclasses.dataclass(kw_only=True)
class Event:
time: int

def __lt__(self, other):
    return self.time < other.time
fossil granite
#

Ah, i didn't consider mixing them

#

That would work, with a tiny bit more boilerplate ๐Ÿ˜›

#

Thanks @patent spire

humble comet
#

if it's acceptable to not include x and y in == equality comparisons, you can exclude them from ordering with field(compare=False), as in: ```py
from dataclasses import dataclass, field

@dataclass(order=True)
class Widget:
id: int
x: int = field(compare=False) # NOTE: implicitly removes from hash unless hash=True is set
y: int = field(compare=False)

assert Widget(1, 2, 3) < Widget(3, 2, 1)
assert Widget(1, 2, 3) == Widget(1, 4, 5)``` but if equality of your coordinates does matter then i'd go with offby1's suggestion too

errant totemBOT
#
Python help channel closed

This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.