Ticket #4613 enhancement new
FancyHashMixin: like FancyEqMixin but for __hash__
| Reported by: | lvh | Owned by: | |
|---|---|---|---|
| Priority: | lowest | Milestone: | |
| Component: | core | Keywords: | |
| Cc: | awclinford@… | Branch: | |
| Author: | Launchpad Bug: |
Description
Right now FancyEqMixin has a somewhat strange (and I would argue broken) behavior:
>>> from twisted.python.util import FancyEqMixin >>> class C(object, FancyEqMixin): ... compareAttributes = "a", ... ... def __init__(self, a): ... self.a = a ... >>> c1, c2 = C(1), C(1) >>> c1 is not c2 and c1 == c2 and hash(c1) != hash(c2) True
(The problem being that C.__hash__ == object.__hash__; which hashes on id.)
I'm fairly certain that c1 == c2 ==> hash(c1) == hash(c2). I propose that FancyEqMixin grows a __hash__ method that raises NotImplementedError. This will not break backwards compatibility; when it does break existing code that code was wrong anyway. The problem with that is that if something else *does* implement a working __hash__ it's hard to guarantee that the good __hash__ is lower in the MRO (eg gets used), plus it breaks cooperative MI if the good __hash__ uses __hash__es from further up in the MRO. Not sure what the right way to fix that is. I think you should implement __hash__ on the level you introduce the mixin anyway?
thrashold has suggested __hash__ = None on IRC, that would raise TypeError; not really the TypeError I would want (I would expect to see "type x is not hashable" rather than something about None not being callable, but apparently this is how you disable hash?
This is new in 2.6:
Changed in version 2.6: __hash__ may now be set to None to explicitly flag instances of a class as unhashable.
http://docs.python.org/reference/datamodel.html#object.__hash__
2.6 actually does the right thing automagically (which is *this*), but only on new-style classes, it appears.
Anyway, I propose a FancyHashMixin that has a hashAttributes classattr like FEM has a compareAttributes classattr and then hashes on the value of [getattr(self, v) for v in self.hashAttributes]. I think it should also subclass FEM; if we can count on cooperative MI (which we can't due to old-style classes) the default value for hashAttributes could be compareAttributes, since in 90% of cases they will be the same (as long as hashAttributes is a subset of compareAttributes it will still behave correctly).
Ceterum censeo geventem et medusam esse delendam.

