Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why and how are Python functions hashable?

Tags:

python

hash

I recently tried the following commands in Python:

>>> {lambda x: 1: 'a'} {<function __main__.<lambda>>: 'a'}  >>> def p(x): return 1 >>> {p: 'a'} {<function __main__.p>: 'a'} 

The success of both dict creations indicates that both lambda and regular functions are hashable. (Something like {[]: 'a'} fails with TypeError: unhashable type: 'list').

The hash is apparently not necessarily the ID of the function:

>>> m = lambda x: 1 >>> id(m) 140643045241584 >>> hash(m) 8790190327599 >>> m.__hash__() 8790190327599 

The last command shows that the __hash__ method is explicitly defined for lambdas, i.e., this is not some automagical thing Python computes based on the type.

What is the motivation behind making functions hashable? For a bonus, what is the hash of a function?

like image 934
Mad Physicist Avatar asked Jul 22 '16 05:07

Mad Physicist


People also ask

Why are Python sets hashable?

Hashability makes an object usable as a dictionary key and a set member, because these data structures use the hash value internally. All of Python's immutable built-in objects are hashable, while no mutable containers (such as lists or dictionaries) are.

How do you know if a Python item is hashable?

__hash__() method. You can check for that. A warning about this: In Python 2.5 this will give: {'test': 'dict'}is hashable . It can also give a wrong result in newer versions if a class defines __hash__ to raise a TypeError.

Are Python lists hashable?

Python dictionaries only accept hashable data types as a key in a dictionary. A list is not a hashable data type. If you specify a list as a key in a dictionary, you'll encounter a “TypeError: unhashable type: 'list'” error.

What does it mean to be hashable?

An object is said to be hashable if it has a hash value that remains the same during its lifetime.


1 Answers

It's nothing special. As you can see if you examine the unbound __hash__ method of the function type:

>>> def f(): pass ... >>> type(f).__hash__ <slot wrapper '__hash__' of 'object' objects> 

the of 'object' objects part means it just inherits the default identity-based __hash__ from object. Function == and hash work by identity. The difference between id and hash is normal for any type that inherits object.__hash__:

>>> x = object() >>> id(x) 40145072L >>> hash(x) 2509067 

You might think __hash__ is only supposed to be defined for immutable objects, and you'd be almost right, but that's missing a key detail. __hash__ should only be defined for objects where everything involved in == comparisons is immutable. For objects whose == is based on identity, it's completely standard to base hash on identity as well, since even if the objects are mutable, they can't possibly be mutable in a way that would change their identity. Files, modules, and other mutable objects with identity-based == all behave this way.

like image 72
user2357112 supports Monica Avatar answered Oct 11 '22 00:10

user2357112 supports Monica