0

Is there a nice way to control the hash of a method of a Python class. Let's say I have the following example:

class A:
    def hello(self, arg):
        print(arg)
    def __hash__(self):
        return 12345

a = A()
b = A()

hash(a.hello) == hash(b.hello) >>> False

Now I'm vaguely aware of why that is. Internally methods are functions that have a class reference with some magic to it but basically, they are just functions that (probably) inherit from object. So the __hash__ method of class A is only relevant to its own instances. However, while this makes sense at first, I did realize that in Python 3.7 the example above evaluates to True, while in 3.8 it is False.

Does anyone: (1) know how to achieve this behavior in > 3.7 (thus controlling the hash of a method), and, (2) know why and what changed between the two versions (I am starting to doubt my sanity tbh)?

meow
  • 2,062
  • 2
  • 17
  • 27
  • 1
    Are you sure that what you're observing isn't just the consequence of random seeding of the hasher? See https://stackoverflow.com/q/27522626/3141234 – Alexander Feb 03 '23 at 02:40
  • In any case, these are two completely different bound `method` objects. Why do you expect/want their hash values to be the same? – Alexander Feb 03 '23 at 02:42
  • @Alexander I thought so too about the hash, could well be and is the thing that makes the most sense. Regarding the second question, for some thread-local cashing applications and I found it quite interesting to be honest (of course there are other ways). Would be sweet if there was an easy way. I got it to work using descriptors but it's not what I would consider a nice solution. – meow Feb 03 '23 at 03:09
  • this is because `a.hello is not b.hello`, indeed, `a.hello is not a.hello` and you are right, bound-method objects inherit their hashing behavior from `object`, so they hash based on identity (on the object's `id`) – juanpa.arrivillaga Feb 03 '23 at 03:21
  • Now, what probably happened is not any difference between versions, but the `id` `a.hello` got re-used by `b.hello`, since `a.hello` ceased to exist by the time `b.hello` came into existence, and `id`'s are only guaranteed unique for the lifetime of the object. – juanpa.arrivillaga Feb 03 '23 at 03:24
  • 1
    anyway, using descriptors is probably the most reaosnable way, basically, you can just steal https://docs.python.org/3/howto/descriptor.html#functions-and-methods and have `class MethodType` implement `__hash__` and `__eq__` by deferring to the function they wrap. so just `return self.__func__.__hash__()` and `return self.__func__.__eq__(other)` – juanpa.arrivillaga Feb 03 '23 at 03:34
  • @juanpa.arrivillaga yeah I know about the descriptors path, it does work but is a bit cumbersome and ugly to use, but probably the best we got :) – meow Feb 03 '23 at 10:43

0 Answers0