attrs icon indicating copy to clipboard operation
attrs copied to clipboard

`__hash__` with custom `__eq__`

Open AdrianSosic opened this issue 5 months ago • 2 comments

Hi, I know that hashing is a complicated topic and I've seen the sections in the docs / API reference + the resources linked therein. However, I'm still somewhat unclear about what is expected to happen in the situation described at the end.

Is this:

  • expected?
  • an attrs issue?
  • a ruff issue?

And regardless of which it is, how to properly resolve it? The attrs docs say the following:

Image

My understanding is that we are facing the highlighted case here, but I don't see the fallback to id-based hashing (regardless of whether I explicitly declare eq=True/False in the decorator) unless I add the __hash__ = object.__hash__ line.

Issue

from attrs import define


@define(frozen=True)
class C:
    x: int

    def __eq__(self, value):
        return self.x in (0, value)

Gives the following ruff error:

PLW1641 Object does not implement `__hash__` method
 --> dbg.py:5:7
  |
4 | @define(frozen=True)
5 | class C:
  |       ^
6 |     x: int
  |

AdrianSosic avatar Aug 29 '25 08:08 AdrianSosic

These are multiple questions – the main one for me is:

but I don't see the fallback to id-based hashing (regardless of whether I explicitly declare eq=True/False in the decorator

This code prints False, True, True as the docs write – what am I missing?

from __future__ import annotations

from attrs import define, field, frozen

@frozen
class C:
    x: int

@define(eq=False)
class CD:
    x: int

@frozen(eq=False)
class CF:
    x: int

print(C.__hash__ is object.__hash__)
print(CD.__hash__ is object.__hash__)
print(CF.__hash__ is object.__hash__)

BTW, coincidentally I've been refactoring this code and I think it's now clearer. You may want to have a peek at #1454.

hynek avatar Sep 08 '25 16:09 hynek

Sorry for the late reply, have been traveling.

So probably it is a ruff bug then (perhaps you can confirm), but take your example and add a custom __eq__ method to any of your classes (e.g. like the one I defined in my example) and you'll get the ruff false-positive (?) error I pasted above.

AdrianSosic avatar Sep 30 '25 10:09 AdrianSosic