Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New-style classes with __eq__ but not __hash__ are hashable #39631

Closed
edloper mannequin opened this issue Nov 30, 2003 · 4 comments
Closed

New-style classes with __eq__ but not __hash__ are hashable #39631

edloper mannequin opened this issue Nov 30, 2003 · 4 comments
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs)

Comments

@edloper
Copy link
Mannequin

edloper mannequin commented Nov 30, 2003

BPO 851449
Nosy @rhettinger

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = <Date 2003-12-02.07:17:39.000>
created_at = <Date 2003-11-30.05:40:01.000>
labels = ['interpreter-core']
title = 'New-style classes with __eq__ but not __hash__ are hashable'
updated_at = <Date 2003-12-02.07:17:39.000>
user = 'https://bugs.python.org/edloper'

bugs.python.org fields:

activity = <Date 2003-12-02.07:17:39.000>
actor = 'rhettinger'
assignee = 'none'
closed = True
closed_date = None
closer = None
components = ['Interpreter Core']
creation = <Date 2003-11-30.05:40:01.000>
creator = 'edloper'
dependencies = []
files = []
hgrepos = []
issue_num = 851449
keywords = []
message_count = 4.0
messages = ['19206', '19207', '19208', '19209']
nosy_count = 2.0
nosy_names = ['rhettinger', 'edloper']
pr_nums = []
priority = 'normal'
resolution = 'duplicate'
stage = None
status = 'closed'
superseder = None
type = None
url = 'https://bugs.python.org/issue851449'
versions = ['Python 2.3']

@edloper
Copy link
Mannequin Author

edloper mannequin commented Nov 30, 2003

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

@edloper edloper mannequin closed this as completed Nov 30, 2003
@edloper edloper mannequin added the interpreter-core (Objects, Python, Grammar, and Parser dirs) label Nov 30, 2003
@edloper edloper mannequin closed this as completed Nov 30, 2003
@edloper edloper mannequin added the interpreter-core (Objects, Python, Grammar, and Parser dirs) label Nov 30, 2003
@rhettinger
Copy link
Contributor

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

@edloper
Copy link
Mannequin Author

edloper mannequin commented Dec 1, 2003

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

@rhettinger
Copy link
Contributor

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__.

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs)
Projects
None yet
Development

No branches or pull requests

1 participant