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