Reputation: 37461
I have a type where equality comparison doesn't make sense. Explicitly comparing two instances of this type for either reference or value equality would indicate a logic error in the calling code.
Is it bad to define __eq__
to raise an exception? Are there pitfalls to this? Is it implicitly called as a part of some common operation?
In a language like Haskell I would simply not implement the Equal typeclass, and attempting the comparison would be a compile error. Since python is completely dynamic, do I have an option that would help encourage correct usage if this definition isn't a good idea?
I could return NotImplemented
, but then it does fallback comparisons that will ultimately result in an identity comparison if the RHS also returns NotImplemented
, and I still don't want that.
Upvotes: 5
Views: 796
Reputation:
Making __eq__
throw will prevent your class from being used as keys in a dictionary. Dictionaries are a common way to "attach" arbitrary values to reference-comparable objects.
For example:
class NotComparableAtAll:
def __eq__(self, other):
raise ValueError ('haha')
cache = { }
x, y = NotComparableAtAll (), NotComparableAtAll ()
cache[x] = 1
cache[y] = 2
This fails, saying that the type is not hashable.
However, if you do add __hash__
to the class (default is removed as soon as you define __eq__
), the example appears to work fine almost all the time, only it can unpredictably fail if two distinct objects turn out to have the same hash on some machine. To reproduce it, define __hash__
to always return 0 (which does conform to __hash__
requirements).
Additionally, overriding __eq__
like that breaks some standard functions where comparison by reference would be useful:
my_list = [x, y]
my_list.remove (y)
This can of course be deemed another case of "these objects must really not be compared", but I guess one could come up with other similar examples where comparison is somehow nested inside a different useful operation.
Upvotes: 5