twisted / twisted/twisted

FancyHashMixin: like FancyEqMixin but for __hash__

Open
#4,613 9 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

core enhancement new priority-lowest
Dominant language
Python
Stars
6k
Forks
1.2k
Avg merge
2d 10h
Merged PRs (30d)
10

Description

lvh's avatar @lvh reported
Trac ID trac#4613
Type enhancement
Created 2010-07-24 13:37:52Z

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.

Attachments:

Searchable metadata
trac-id__4613 4613
type__enhancement enhancement
reporter__lvh lvh
priority__lowest lowest
milestone__ 
branch__ 
branch_author__ 
status__new new
resolution__None None
component__core core
keywords__ 
time__1279978672000000 1279978672000000
changetime__1295883905000000 1295883905000000
version__None None
owner__ 
cc__awclin

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with FancyEqMixin in twisted.python.util and read the issue's discussion of hash, hash = None, cooperative multiple inheritance, and old-style classes. Compare the two attached diffs to understand the proposed directions. Done means reaching a decided design for FancyHashMixin and confirming that equality and hashing remain consistent under the documented Python behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.