Pre-PEP: __hashable__
Delaney, Timothy
tdelaney at avaya.com
Tue Dec 10 18:00:18 EST 2002
There have been a number of suggestions in the past along the lines of
creating an `immutable()` function.
I would like to propose an alternative, prompted by the new `sets` module in
2.3.
In the `sets` module, when a set is to be put into something like a
dictionary, instead a new immutable set is created which will compare equal
to a set with the same members.
I think this can be generalised, with no loss of functionality or backwards
compatibility.
A new builtin function `hashable(obj)` and a new magic method
`__hashable__(self)` are added.
`hashable(obj)` takes a single object. If the object has a `__hashable__()`
method it is called, and the result returned, otherwise the object itself is
returned:
def hashable (obj):
try:
return obj.__hashable__()
except AttributeError:
return self
The object returned from `__hashable__(self)` should be hashable i.e.
`hash(obj)` will not throw an exception. Note though that that is not
enforced above.
The existing `hash(obj)` builtin would be modified as (conceptually):
def hash (obj):
try:
return hashable(obj).__hash__()
except AttributeError:
raise TypeError('unhashable type')
What does this gain us? It allows a mutable type to have a snapshot taken an
used as a dictionary key, or other place which requires a hashable instance.
The classic example is a list. Currently if I want to use a list as a
dictionary key, I first have to convert it to a tuple explicitly. The result
of this is that the key no longer compares equal to the original list.
With this proposal, a snapshot of the list could be taken and stored in the
dictionary, without the need to convert it to another type explicitly.
class immutablelist (tuple):
def __eq__(self, other):
return list(self) == other
class list (object):
def __hashable__(self):
return immutablelist(self)
l = [1, 2, 3]
d = {}
d[l] = 1
for k in d:
assert k == l
l[0] = 0
for k in d:
assert k != l
l[0] = 1
for k in d:
assert k == l
This is the same behaviour that sets will exhibit (I think I've got the
syntax right, going off memory):
s = set(1, 2, 3) # at a later date, s = {1, 2, 3}
d = {}
d[s] = s
for k in d:
assert k == s
s -= set(1)
for k in d:
assert k != s
s += set(1)
for k in d:
assert k == s
The only cases where there is backwards-compatibility issues that I can
think of are cases where currently an exception would be thrown.
Tim Delaney
More information about the Python-list
mailing list