Issue 851449: New-style classes with eq but not hash are hashable (original) (raw)
Issue851449
Created on 2003-11-30 05:40 by edloper, last changed 2022-04-11 14:56 by admin. This issue is now closed.
Messages (4) | ||
---|---|---|
msg19206 - (view) | Author: Edward Loper (edloper) * ![]() |
Date: 2003-11-30 05:40 |
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 | ||
msg19207 - (view) | Author: Raymond Hettinger (rhettinger) * ![]() |
Date: 2003-12-01 10:52 |
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 | ||
msg19208 - (view) | Author: Edward Loper (edloper) * ![]() |
Date: 2003-12-01 14:49 |
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 | ||
msg19209 - (view) | Author: Raymond Hettinger (rhettinger) * ![]() |
Date: 2003-12-02 07:17 |
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__. |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022-04-11 14:56:01 | admin | set | github: 39631 |
2003-11-30 05:40:01 | edloper | create |