diff --git a/changelog.d/1310.breaking.md b/changelog.d/1310.breaking.md new file mode 100644 index 000000000..a8f0a7228 --- /dev/null +++ b/changelog.d/1310.breaking.md @@ -0,0 +1,3 @@ +Speed up the generated `__eq__` methods significantly by generating a chain of attribute comparisons instead of constructing and comparing tuples. +This change arguably makes the behavior more correct, +but changes it if an attribute compares equal by identity but not value, like `float('nan')`. diff --git a/docs/comparison.md b/docs/comparison.md index 79786e9e1..7dd6ca581 100644 --- a/docs/comparison.md +++ b/docs/comparison.md @@ -5,7 +5,10 @@ For that, *attrs* writes `__eq__` and `__ne__` methods for you. Additionally, if you pass `order=True`, *attrs* will also create a complete set of ordering methods: `__le__`, `__lt__`, `__ge__`, and `__gt__`. -Both for equality and order, *attrs* will: +For equality, *attrs* will generate a statement comparing the types of both instances, +and then comparing each attribute in turn using `==`. + +For order, *attrs* will: - Check if the types of the instances you're comparing are equal, - if so, create a tuple of all field values for each instance, diff --git a/src/attr/_make.py b/src/attr/_make.py index 097534fbb..89c2d569f 100644 --- a/src/attr/_make.py +++ b/src/attr/_make.py @@ -1864,20 +1864,20 @@ def _make_eq(cls, attrs): globs = {} if attrs: lines.append(" return (") - others = [" ) == ("] for a in attrs: if a.eq_key: cmp_name = f"_{a.name}_key" # Add the key function to the global namespace # of the evaluated function. globs[cmp_name] = a.eq_key - lines.append(f" {cmp_name}(self.{a.name}),") - others.append(f" {cmp_name}(other.{a.name}),") + lines.append( + f" {cmp_name}(self.{a.name}) == {cmp_name}(other.{a.name})" + ) else: - lines.append(f" self.{a.name},") - others.append(f" other.{a.name},") - - lines += [*others, " )"] + lines.append(f" self.{a.name} == other.{a.name}") + if a is not attrs[-1]: + lines[-1] = f"{lines[-1]} and" + lines.append(" )") else: lines.append(" return True") diff --git a/tests/strategies.py b/tests/strategies.py index cbbf5b334..cb3f326ea 100644 --- a/tests/strategies.py +++ b/tests/strategies.py @@ -75,7 +75,7 @@ def _create_hyp_nested_strategy(draw, simple_class_strategy): bare_attrs = st.builds(attr.ib, default=st.none()) int_attrs = st.integers().map(lambda i: attr.ib(default=i)) str_attrs = st.text().map(lambda s: attr.ib(default=s)) -float_attrs = st.floats().map(lambda f: attr.ib(default=f)) +float_attrs = st.floats(allow_nan=False).map(lambda f: attr.ib(default=f)) dict_attrs = st.dictionaries(keys=st.text(), values=st.integers()).map( lambda d: attr.ib(default=d) )