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.
All built-in immutable objects, like tuples, are hashable while mutable containers like lists and dictionaries are not hashable.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With