Make a list of ints hashable in python

2020-07-17 04:47发布

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.

标签: python hash
2条回答
唯我独甜
2楼-- · 2020-07-17 05:07

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.

查看更多
SAY GOODBYE
3楼-- · 2020-07-17 05:20

You also can create hashable list.

from collections import Iterable

class hash_list(list): 
    def __init__(self, *args): 
        if len(args) == 1 and isinstance(args[0], Iterable): 
            args = args[0] 
        super().__init__(args) 

    def __hash__(self): 
        return hash(e for e in self)
查看更多
登录 后发表回答