Skip to content

Commit f3632b4

Browse files
miss-islingtonAniketsyvstinner
authored
[3.15] gh-138991: Update dataclass documentation for new eq behavior in Python 3.13 (GH-139007) (#151372)
gh-138991: Update dataclass documentation for new eq behavior in Python 3.13 (GH-139007) And add tests. (cherry picked from commit 402668b) Co-authored-by: Aniket <148300120+Aniketsy@users.noreply.github.com> Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent f3fd7b8 commit f3632b4

2 files changed

Lines changed: 67 additions & 5 deletions

File tree

Doc/library/dataclasses.rst

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,25 @@ Module contents
100100
ignored.
101101

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

107-
If the class already defines :meth:`!__eq__`, this parameter is
108-
ignored.
105+
This method compares the class by comparing each field in order. Both
106+
instances in the comparison must be of the identical type.
107+
108+
If the class already defines :meth:`!__eq__`, this parameter is ignored.
109+
110+
.. versionchanged:: 3.13
111+
The generated ``__eq__`` method now compares each field individually
112+
(for example, ``self.a == other.a and self.b == other.b``), rather than
113+
comparing tuples of fields as in previous versions.
114+
115+
This change makes the comparison faster but it may alter results in cases
116+
where attributes compare equal by identity but not by value (such as
117+
``float('nan')``).
118+
119+
In Python 3.12 and earlier, the comparison was performed by creating
120+
tuples of the fields and comparing them (for example,
121+
``(self.a, self.b) == (other.a, other.b)``).
109122

110123
- *order*: If true (the default is ``False``), :meth:`~object.__lt__`,
111124
:meth:`~object.__le__`, :meth:`~object.__gt__`, and :meth:`~object.__ge__` methods will be

Lib/test/test_dataclasses/__init__.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2792,6 +2792,55 @@ def __eq__(self, other):
27922792
self.assertEqual(C(1), 5)
27932793
self.assertNotEqual(C(1), 1)
27942794

2795+
def test_eq_field_by_field(self):
2796+
@dataclasses.dataclass
2797+
class Point:
2798+
x: int
2799+
y: int
2800+
2801+
p1 = Point(1, 2)
2802+
p2 = Point(1, 2)
2803+
p3 = Point(2, 1)
2804+
self.assertEqual(p1, p2)
2805+
self.assertNotEqual(p1, p3)
2806+
2807+
def test_eq_type_check(self):
2808+
@dataclasses.dataclass
2809+
class A:
2810+
x: int
2811+
2812+
@dataclasses.dataclass
2813+
class B:
2814+
x: int
2815+
2816+
a = A(1)
2817+
b = B(1)
2818+
self.assertNotEqual(a, b)
2819+
2820+
def test_eq_custom_field(self):
2821+
class AlwaysEqual(int):
2822+
def __eq__(self, other):
2823+
return True
2824+
2825+
@dataclasses.dataclass
2826+
class Foo:
2827+
x: AlwaysEqual
2828+
y: int
2829+
2830+
f1 = Foo(AlwaysEqual(1), 2)
2831+
f2 = Foo(AlwaysEqual(2), 2)
2832+
self.assertEqual(f1, f2)
2833+
2834+
def test_eq_nan_field(self):
2835+
@dataclasses.dataclass
2836+
class D:
2837+
x: float
2838+
2839+
nan = float('nan')
2840+
d1 = D(nan)
2841+
d2 = D(nan)
2842+
self.assertNotEqual(d1, d2)
2843+
27952844

27962845
class TestOrdering(unittest.TestCase):
27972846
def test_functools_total_ordering(self):

0 commit comments

Comments
 (0)