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