1

In python, a tuple containing a mutable object (like a list) is not hashable.

In [1]: t = (1,2,[3])                                                           
In [2]: hash(t)                                                                 
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-8a01c6d8dba1> in <module>
----> 1 hash(t)
TypeError: unhashable type: 'list'

But why it is not correct for functions with mutable params?

In [5]: def f(a: List = [1]): 
   ...:     a += a 
   ...:     return a 
   ...:                                                                         
In [6]: hash(f)                                                                 
Out[6]: 8731603958411

Every time this function is called, the a parameter changes, but the function f is still hashable and considered to be immutable. What is the difference between functions and tuples in this case?

Amir reza Riahi
  • 1,540
  • 2
  • 8
  • 34
  • Because *the function* is still *the function*, even if it returns a different value every time you call it. – deceze Sep 08 '21 at 06:53
  • The (mutable) default values of arguments apparently aren't taken into account when computing the hash of a function object. – AKX Sep 08 '21 at 06:54
  • 2
    @deceze A list is still the same list by identity if mutated, so that argument isn't quite waterproof if you ask me... – AKX Sep 08 '21 at 06:55
  • 1
    @AKX Well, what *are* you hashing when hashing something? You want to get a representative value of your hashee. Nobody would expect two lists with different contents to have the same hash, as that wouldn't be "representative". But a function can reasonably only be "represented" by its identity. There are infinitely many ways in which a function may return a different result on each call, so the internal state or return value of a function can't be considered part of its "representation". – deceze Sep 08 '21 at 07:06
  • 1
    @deceze Consider `def f(x=[]): pass`. Why would `f` be hashable if `f.__defaults__` isn't? – AKX Sep 08 '21 at 07:17
  • 1
    @AKX Consider `l = []; def f(): return l` — Why should that be any more or less hashable? – deceze Sep 08 '21 at 07:19
  • @deceze Because we're not symbolically executing the opcodes to find a `get global` opcode... Fair, I guess that's a philosophical discussion whether only pure functions (or those whose environment is also hashable) should be hashable... I guess (as usual) Python has taken the practical approach to limit what is taken into account when hashing a function. – AKX Sep 08 '21 at 07:23
  • 1
    @AKX Yeah, again, the question is "what does a hash tell you?" It's by definition that in the case of a function, it depends on object identity and object identity only. Anything else would be close to impossible to evaluate for Python. Python isn't a functional language, so "purity" is extremely difficult if not impossible to evaluate. It's also questionable whether that's what you want to know when taking a hash of a function. Should two functions which would return the same result when called be considered the same? How does that even work with passing arguments…? – deceze Sep 08 '21 at 07:26

0 Answers0