Pre-PEP: __hashable__
Delaney, Timothy
tdelaney at avaya.com
Tue Dec 10 21:39:41 EST 2002
> From: Chad Netzer [mailto:cnetzer at mail.arc.nasa.gov]
>
> > `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:
>
> Why-oh-why would it return self if an object isn't hashable? That
> alone makes the whole thing worthless. It should throw an
> expection.
I thought about having it throw an exception here - but feel it should be up
to hash() to actually do that. Whilst it *feels* right to have it done
inside hashable(), you will end up calling __hash__ multiple times in the
most common case.
def hashable (obj):
try:
obj = obj.__hashable__()
except AttributeError:
pass
try:
obj.__hash__()
except AttributeError:
raise TypeError('unhashable type')
def hash (obj):
try:
return obj.__hash__()
except AttributeError:
return hashable(obj).__hash__()
> > 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.
>
> But what happens when I want to make the object unhashable again?
> If the answer is, "You don't. You have to create a new object.",
> then I would ask, where could this possibly be useful?
Precisely, the answer is "You don't". A particular instance could of course
have its own way of making a mutable instance - and in fact, that's what I
do in my very abbreviated implementation of immutablelist.
> For example, I use a dict as a key. Later I want to retrieve
> something using that key. But, my mutable dictionary has changed.
> So my key is lost.
That is true. Because the key you used is a snapshot of the dictionary at
the time you created it.
If at a later stage your dictionary then changes to once again match the
key, it will.
> Or, suppose I use a list as a key. Then I want to retrieve it,
> but my list has changed?
As above.
> Well, perhaps I have a tuple that has all
> the same objects as a list. Should that allow me to retrieve the
> list key?
No. Hence the introduction of the immutablelist class. This will compare
equal to a list with the same elements, and also equal to anything which
would compare equal to such a list. Exactly the same semantics as using a
tuple as the key (or an int, or whatever).
The difference is that you can then use that list for some other purpose,
and at a later stage it or something else may match that snapshot.
> If so, it needs to hash to the same key (and thus, I
> couldn't have BOTH a list and tuple as keys in the same dict(), and
> have them be distinct). But lists and tuples with the same entries
> do not compare the same, so that would be wierd. Should that be
> "fixed", changing Python semantics?
Of course you can. immutablelist would not compare equal to a tuple with the
same elements.
I could create a class which compared equal to a tuple with the same
elements. Would you then complain that you couldn't have a tuple and an
instance of that class with the same elements in a dict?
> It would be nice to conceptually be able to have an immutable
> dictionary type (and perhaps someone has made one), to use as a
> key. But there are workarounds already that can be used (id() as
> an example, thought it takes care)
id() is definitely *not* valid for this, for 2 reasons:
1. It can be reused.
2. It would only match the exact same object (unless it gets reused).
Consider the following (currently non-valid) code sample:
d1 = {1:2}
d2 = {1:2}
d = {d1: 1}
assert d[d1] == d[d2]
> That is my knee-jerk reaction. I won't say it can't be done, or
> wouldn't be useful. But I think you haven't yet even begun to
> think of all the ramifications. (or else, I missed something; I'm
> quite daft)
There is already precedent for this (in 2.3) - this is what sets do. Sets
are mutable - but when you want to use them as (for example) a dictionary
key, a snapshot is taken (an immutable set). You can then continue modifying
the original set - the key stays the same. If at some stage the set and the
snapshot match up, you could retrieve your value from the dictionary.
Believe me - I've thought seriously about the ramifications of this. It's
quite possible I've missed something.
More information about the Python-list
mailing list