1

I understand that mutable objects can't be hashed, but why should this also be true of the methods on mutable objects, especially given that they compare as expected?

For example:

>>> d1, d2 = {}, {}
>>> d1.keys == d2.keys
False
>>> set([d1.keys])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'

In the example, d1.keys is not itself the dict the exception complains about. Simply having a dict as an attribute is obviously no general bar to being hashable, as instances of user-created classes, by default, are both hashable and have a __dict__ attribute. A method object will not itself change in any meaningful way over it's lifetime, whether or not its __self__ component is mutable. Is there some way I'm not seeing that hashing methods on mutable object breaks the contract implied by hashing?

Below is my attempt at a wrapper that ensures a method will be hashable and also plays nice with regular method objects. For a normally unhashable method, the hash value is calculated from a tuple containing the method implementation object and the id of the method's __self__ component. Any reason this might blow up on me?

class HashableMethod(object):
    __slots__ = ['_method', '_hashval', '__weakref__']

    def __init__(self, method):
        self._method = method

    def __getattr__(self, name):
        return getattr(self._method, name)

    def __call__(self, *args, **kwds):
        return self._method(*args, **kwds)

    def __eq__(self, other):
        return self._method == other

    def __hash__(self):
        try:
            return self._hashval
        except AttributeError: # self._hashval is not yet calculated
            pass

        meth = self._method
        try:
            hashval = self._hashval = hash(meth)
        except TypeError: # self._method is not ordinarily hashable
            obj = meth.__self__
            try:
                func = meth.__func__
            except AttributeError: # self._method is builtin
                func = getattr(type(obj), meth.__name__)
            hashval = self._hashval = hash((func, id(obj)))
        return hashval
Community
  • 1
  • 1
snowy plover
  • 146
  • 1
  • 3
  • If you're looking for a comprehensive critique of working code, you might be better off posting this at http://codereview.stackexchange.com – Robert Harvey Sep 06 '13 at 05:47
  • 1
    I think it's just that no one cares. If you have a compelling use case for hashing bound methods, you could probably get the behavior changed. – user2357112 Sep 06 '13 at 06:21
  • You are overgeneralizing, the reason you can't hash `dict.keys` is simply that it is a builtin function and nobody cared to make it hashable. You can hash any kind of user defined function. – Jochen Ritzel Sep 06 '13 at 12:24
  • @JochenRitzel - Yes, user-defined functions are hashable, but so are builtin ones: `hash(isinstance)` works just fine. However, I am concerned with methods, and a bound method on a mutable object (including subclasses) is not hashable whether the method is user-defined or builtin. This is only a problem for bound methods: `hash(dict.keys)` works, `hash({}.keys)` does not. – snowy plover Sep 07 '13 at 01:09

0 Answers0