[ 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