Skip to content
Open
19 changes: 14 additions & 5 deletions Doc/library/dataclasses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,21 @@ Module contents
ignored.

- *eq*: If true (the default), an :meth:`~object.__eq__` method will be
generated. This method compares the class as if it were a tuple
of its fields, in order. Both instances in the comparison must
be of the identical type.
generated.

If the class already defines :meth:`!__eq__`, this parameter is
ignored.
This method compares the class by comparing each field in order. Both
instances in the comparison must be of the identical type.

.. versionchanged:: 3.13
The generated ``__eq__`` method now compares each field individually
(for example, ``self.a == other.a and self.b == other.b``), rather than
comparing tuples of fields as in previous versions.

In Python 3.12 and earlier, the comparison was performed by creating
tuples of the fields and comparing them (for example,
``(self.a, self.b) == (other.a, other.b)``).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how the formatting would work out, but I think the note about 3.12 should be part of the versionchanged note. Also, might it be worth mentioning why this matters? It would matter for any value V where V!=V. Of the built in types/values, I think this only matters for NaN (but I'm willing to be corrected). On the other hand, maybe this would be getting into too much detail for the documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For formatting, @StanFromIreland suggested wrapping lines to 79 characters.
I’ll update these changes for now:
-Moving the sentence before the versionchanged note.

For the other changes you suggested, I’d like to wait until they are confirmed before updating.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By "formatting", I meant possibly multiple paragraphs in a versionadded block. I don't know if that's possible (or desirable).


If the class already defines :meth:`!__eq__`, this parameter is ignored.

- *order*: If true (the default is ``False``), :meth:`~object.__lt__`,
:meth:`~object.__le__`, :meth:`~object.__gt__`, and :meth:`~object.__ge__` methods will be
Expand Down
49 changes: 49 additions & 0 deletions Lib/test/test_dataclasses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2597,6 +2597,55 @@ def __eq__(self, other):
self.assertEqual(C(1), 5)
self.assertNotEqual(C(1), 1)

def test_eq_field_by_field(self):
@dataclasses.dataclass
class Point:
x: int
y: int

p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(2, 1)
self.assertTrue(p1 == p2)
self.assertFalse(p1 == p3)

def test_eq_type_check(self):
@dataclasses.dataclass
class A:
x: int

@dataclasses.dataclass
class B:
x: int

a = A(1)
b = B(1)
self.assertFalse(a == b)

def test_eq_custom_field(self):
class AlwaysEqual(int):
def __eq__(self, other):
return True

@dataclasses.dataclass
class Foo:
x: AlwaysEqual
y: int

f1 = Foo(AlwaysEqual(1), 2)
f2 = Foo(AlwaysEqual(2), 2)
self.assertTrue(f1 == f2)

def test_eq_nan_field(self):
@dataclasses.dataclass
class D:
x: float

nan = float('nan')
d1 = D(nan)
d2 = D(nan)
self.assertFalse(d1 == d2)


class TestOrdering(unittest.TestCase):
def test_functools_total_ordering(self):
Expand Down
Loading