Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make a list of ints hashable in python

Tags:

python

hash

I have a lists of integers that I would like to use as keys in python dictionaries. I'm caching results from a function(s) that takes a list of ints as input. My current solution:

list_of_ints = [1,20,3,4]
key = str(sorted(list_of_ints))[1:-1].replace(' ','')

which generates the key '1,3,4,20'. Seems like there should be a faster/prettier/more-pythonic way to do this.

like image 585
I.P. Freeley Avatar asked Jan 26 '16 00:01

I.P. Freeley


People also ask

Is list hashable in Python?

All built-in immutable objects, like tuples, are hashable while mutable containers like lists and dictionaries are not hashable.

Are integers hashable in Python?

We can verify that strings and integers are both hashable by calling the hash() method on them. So hash(s) will not error, it'll spit out a number. And this number just represents the hash of s and you can imagine hashing just changes the object into a number. 02:40 And each time we hash it, we get that same value.

How do I make a hashable object in Python?

An object hash is an integer number representing the value of the object and can be obtained using the hash() function if the object is hashable. To make a class hashable, it has to implement both the __hash__(self) method and the aforementioned __eq__(self, other) method. As with equality, the inherited object.

Are ints hashable?

Many types in the standard library conform to Hashable : Strings, integers, floating-point and Boolean values, and even sets are hashable by default. Some other types, such as optionals, arrays and ranges automatically become hashable when their type arguments implement the same.


1 Answers

Just use a tuple as a key. Tuples are immutable and hashable, so they're useful as dictionary keys.

list_of_ints = [1, 20, 3, 4]
# tuple(list_of_ints) == (1, 20, 3, 4)

some_dict = {tuple(list_of_ints): "some value", ...}

Notably they DO care about order, so [1, 20, 3, 4] won't produce the same value as [1, 3, 20, 4]

You could even create a container that does this for you.

class MyDict(dict):
    def __getitem__(self, key):
        key = tuple(sorted(key))
        return super().__getitem__(key)
    # similar for pop, get, setdefault, update....

>>> d = MyDict()
>>> d[1,2,3] = 4
>>> d[3,2,1]
4

Don't try to serialize it yourself. If you do, don't use string manipulation -- it's too ugly. If you are sincerely memory starved or you have hundreds of thousands of these records, you could save insignificant space by serializing like:

def my_serialize(key_nums: list):
    key_nums = sorted(key_nums)
    base = max(key_nums)
    sum_ = 0
    for power, num in enumerate(key_nums):
        sum_ += base**power * num
    return sum_

which should give you a unique (incredibly large!) integer to store that will be smaller in memory than the tuple. Don't do this if you can avoid it -- it's very opaque.


In the comments you mention you will not have duplicate values in the key, so frozenset is definitely what you're looking for.

d = {}
list_of_ints = [1, 20, 3, 4]
d[frozenset(list_of_ints)] = "some value"

frozenset objects are immutable hashable set-like objects. They are order-agnostic and ignore duplicates.

like image 103
Adam Smith Avatar answered Oct 20 '22 18:10

Adam Smith