[ python-Bugs-851449 ] New-style classes with __eq__ but not __hash__ are hashable

SourceForge.net noreply at sourceforge.net
Tue Dec 2 02:17:39 EST 2003


Bugs item #851449, was opened at 2003-11-30 00:40
Message generated for change (Comment added) made by rhettinger
You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=851449&group_id=5470

Category: Python Interpreter Core
Group: Python 2.3
>Status: Closed
>Resolution: Duplicate
Priority: 5
Submitted By: Edward Loper (edloper)
Assigned to: Nobody/Anonymous (nobody)
Summary: New-style classes with __eq__ but not __hash__ are hashable

Initial Comment:
According to the current reference docs, "If [a class] 
defines
__cmp__() or __eq__() but not __hash__(), its 
instances will not be
usable as dictionary keys. [1]  But this doesn't work 
quite like you'd
think for new-style classes:

    Python 2.3 (#1, Sep 13 2003, 00:49:11) 
    [GCC 3.3 20030304 (Apple Computer, Inc. build 
1495)] on darwin
    Type "help", "copyright", "credits" or "license" for 
more information.
    >>> class A(object):
    ...     def __cmp__(self, other): return -1
    >>> print {A():1}
    {<__main__.A object at 0x71cf0>: 1}

The problem is that object defines a default __hash__ 
method:

    >>> print A.__hash__
    <slot wrapper '__hash__' of 'object' objects>

So the dictionary class thinks that the object is 
hashable.  But given
that we've overridden cmp, there's no reason to believe 
that __hash__
is still valid.  The only workaround I've found is to 
manually add a
__hash__ method that raises the appropriate 
exception:

    >>> class A(object):
    ...     def __cmp__(self, other): return -1
    ...     def __hash__(self): 
    ...         raise TypeError, ('%s objects are unhashable' 
% 
    ...                           self.__class__)

But it seems like this should be fixed in Python itself.  I 
can think
of 2 reasonable ways to fix it:

  - change object.__hash__() to raise a TypeError if 
__cmp__ or
    __eq__ is overridden.
  - change hash() to raise a TypeError if given an object 
that
    overrides __cmp__ or __eq__ but not __hash__.

So..  Is this a real bug, or am I missing something?  And 
if so,
what's the prefered place to fix it?  (I'd be happy to try 
to put
together a patch for it, if it is indeed broken.)

-Edward

[1] http://www.python.org/doc/current/ref/
customization.html


----------------------------------------------------------------------

>Comment By: Raymond Hettinger (rhettinger)
Date: 2003-12-02 02:17

Message:
Logged In: YES 
user_id=80475

I should have been clearer.

The bug has been discussed several times before (SF 475877,
660098, and 730087) and while  unresolved leaves us in a
workable position of explicitly defining a nohash function.  

I rechecked my notes, the right way to implement such a
function is to raise a TypeError.  I misrememberes returning
NotImplemented which is the technique for overcoming certain
issues related to __cmp__.



----------------------------------------------------------------------

Comment By: Edward Loper (edloper)
Date: 2003-12-01 09:49

Message:
Logged In: YES 
user_id=195958

Can you point me to the debate?  I searched the python & 
python-dev mailing lists, and only came up with 
statements that suggested that people think that it does 
have the documented behavior.  E.g., "A new-style class 
would NOT become unhashable by implementing __eq__
w/o __hash__, although its INSTANCES would." 
<http://groups.yahoo.com/group/python-
list/message/108397>

Using "return NotImplemented" does *not* seem like the 
right thing to do: if I try to use such an object as a 
dictionary key, it gives the confusing error "TypeError: an 
integer is required," since dict expects hash() to return an 
int.

If this behavior is indeed set in stone, then this should be 
changed to a documentation bug, and the originally 
referenced page 
<://www.python.org/doc/current/ref/customization.html> 
should be updated to describe the actual behavior for new-
style classes.

-Edward


----------------------------------------------------------------------

Comment By: Raymond Hettinger (rhettinger)
Date: 2003-12-01 05:52

Message:
Logged In: YES 
user_id=80475

It has been a subject of debate but the behavior is already
cast in stone.  Anything inheriting from object is hashable
by default.

The preferred way to make things unhashable is:

def __hash__(self)
    return NotImplemented

----------------------------------------------------------------------

You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=851449&group_id=5470



More information about the Python-bugs-list mailing list