[Python-bugs-list] [ python-Bugs-475877 ] Mutable subtype instances are hashable

noreply@sourceforge.net noreply@sourceforge.net
Mon, 03 Dec 2001 09:05:04 -0800


Bugs item #475877, was opened at 2001-10-28 19:24
You can respond by visiting: 
http://sourceforge.net/tracker/?func=detail&atid=105470&aid=475877&group_id=5470

Category: Type/class unification
Group: Python 2.2
Status: Closed
Resolution: Fixed
Priority: 7
Submitted By: Tim Peters (tim_one)
Assigned to: Guido van Rossum (gvanrossum)
Summary: Mutable subtype instances are hashable

Initial Comment:
>>> class D(dictionary): pass
...
>>> d = D()
>>> hash(d)
7920544
>>> id(d)
7920544
>>>

Ditto for instances of list subclasses:

>>> class L(list): pass
...
>>> x = L(range(100))
>>> hash(x)
7928992
>>> id(x)
7928992
>>>

Among other nasties, this lets them get used as 
mutable dict keys.

Reported by Mark J on c.l.py:

"""
From: Mark J <maj64@hotmail.com>
Sent: Sunday, October 28, 2001 10:03 PM
To: python-list@python.org
Subject: Python 2.2b1 hashable dictionary bug?


Since I haven't had a good hit rate at detecting bugs 
vs. features, I thought I'd post here before filing a 
bug report at SourceForge.

Python 2.2b1 (#1, Oct 19 2001, 23:11:09) 
[GCC 2.96 20000731 (Red Hat Linux 7.1 2.96-81)] on 
linux2
Type "help", "copyright", "credits" or "license" for 
more information.
>>> class D(dictionary): pass
... 
>>> d = {}
>>> d2 = D()
>>> d[d2] = "dictionary used as key"
>>> d
{{}: 'dictionary used as key'}
>>> d[D()]="now have two keys that look the same"
>>> d
{{}: 'now have two keys that look the same', 
{}: 'dictionary used as
key'}
>>> d2["key"] = "mutable key in d"
>>> d
{{}: 'now have two keys that look the same', 
{'key': 'mutable key in
d'}: 'dictionary used as key'}
>>> d[d] = "plain dictionary not allowed as key"
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: unhashable type
>>> d2[d2] = "subclassed dictionary allowed though"
>>> d2
{{...}: 'subclassed dictionary allowed 
though', 'key': 'mutable keys'}
>>> #whoa: what just happened: {...} ??

Interesting....  So is this a feature or a bug?
"""

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

>Comment By: Tim Peters (tim_one)
Date: 2001-12-03 09:05

Message:
Logged In: YES 
user_id=31435

Your solution is fine by me!  AFAIK, there's nothing in a 
type object that screams "I'm immutable" or "I'm mutable", 
so guessing whether a thing should be hashable was doomed 
to brittleness -- nothing wrong with being explicit about 
it.

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

Comment By: Guido van Rossum (gvanrossum)
Date: 2001-12-03 08:33

Message:
Logged In: YES 
user_id=6380

Fixed by adding a hash function to list and dict types that
raises an error.  This was by far the simplest solution. 
See checkin comments for discussion (there are plusses and
minuses to this approach).

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

Comment By: Tim Peters (tim_one)
Date: 2001-11-27 13:33

Message:
Logged In: YES 
user_id=31435

Assigned to Guido, since he admitted to thinking about it.

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

Comment By: Guido van Rossum (gvanrossum)
Date: 2001-10-29 11:39

Message:
Logged In: YES 
user_id=6380

Interestingly, in Python list.__hash__ is the same as
object.__hash__, but in C, list.tp_hash is NULL while
object.tp_hash is not.

I think that the best solution is to somehow program an
exception into add_operators that adds a dummy __hash__
wrapper (which always raises an exception) when the tp_hash
field is found to be NULL. (Note that inherit_slots already
contains special-casing for tp_hash.)

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

Comment By: Guido van Rossum (gvanrossum)
Date: 2001-10-28 23:36

Message:
Logged In: YES 
user_id=6380

There's an admin setting that auto-assigns certain
categories.
Documentation gets auto-assugned to Fred; type/class to me;
Regular Expressions to Effbot I believe.

The hash(sys.stdin) result is expected; files are compared
by address and so their hash() is derived from their address
too.

I'll think about the real issue; this has to do with the way
the hash stub gets set. I'm not sure yet whether this is
best fixed by adding more code to slot_tp_hash or to the
code that sticks slot_to_hash in the tp_hash slot.

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

Comment By: Tim Peters (tim_one)
Date: 2001-10-28 19:33

Message:
Logged In: YES 
user_id=31435

I swear I didn't assign this to Guido -- I intended to 
leave this unassigned for now.  That's the second time in 
two weeks I believe SF made up an assignment on one of my 
reports.

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

Comment By: Tim Peters (tim_one)
Date: 2001-10-28 19:28

Message:
Logged In: YES 
user_id=31435

Jeez, hash() has gone off the deep end:

>>> import sys
>>> hash(sys.stdin)
7690032
>>>


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

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