We can use integer, string, tuples as dictionary keys but cannot use list as a key of it .
Because lists are mutable, dict keys need to be hashable, and hashing mutable objects is a bad idea because hash values should be computed on the basis of instance attributes. Example 1: hashing a mutable object where the hash value is based on a mutable characteristic of the object.
It definitely can have a list and any object as value but the dictionary cannot have a list as key because the list is mutable data structure and keys cannot be mutable else of what use are they.
To convert Python Dictionary keys to List, you can use dict. keys() method which returns a dict_keys object. This object can be iterated, and if you pass it to list() constructor, it returns a list object with dictionary keys as elements.
There's a good article on the topic in the Python wiki: Why Lists Can't Be Dictionary Keys. As explained there:
What would go wrong if you tried to use lists as keys, with the hash as, say, their memory location?
It can be done without really breaking any of the requirements, but it leads to unexpected behavior. Lists are generally treated as if their value was derived from their content's values, for instance when checking (in-)equality. Many would - understandably - expect that you can use any list [1, 2]
to get the same key, where you'd have to keep around exactly the same list object. But lookup by value breaks as soon as a list used as key is modified, and for lookup by identity requires you to keep around exactly the same list - which isn't requires for any other common list operation (at least none I can think of).
Other objects such as modules and object
make a much bigger deal out of their object identity anyway (when was the last time you had two distinct module objects called sys
?), and are compared by that anyway. Therefore, it's less surprising - or even expected - that they, when used as dict keys, compare by identity in that case as well.
Why can't I use a list as a dict key in python?
>>> d = {repr([1,2,3]): 'value'}
{'[1, 2, 3]': 'value'}
(for anybody who stumbles on this question looking for a way around it)
as explained by others here, indeed you cannot. You can however use its string representation instead if you really want to use your list.
Just found you can change List into tuple, then use it as keys.
d = {tuple([1,2,3]): 'value'}
The issue is that tuples are immutable, and lists are not. Consider the following
d = {}
li = [1,2,3]
d[li] = 5
li.append(4)
What should d[li]
return? Is it the same list? How about d[[1,2,3]]
? It has the same values, but is a different list?
Ultimately, there is no satisfactory answer. For example, if the only key that works is the original key, then if you have no reference to that key, you can never again access the value. With every other allowed key, you can construct a key without a reference to the original.
If both of my suggestions work, then you have very different keys that return the same value, which is more than a little surprising. If only the original contents work, then your key will quickly go bad, since lists are made to be modified.
Here's an answer http://wiki.python.org/moin/DictionaryKeys
What would go wrong if you tried to use lists as keys, with the hash as, say, their memory location?
Looking up different lists with the same contents would produce different results, even though comparing lists with the same contents would indicate them as equivalent.
What about Using a list literal in a dictionary lookup?
Because lists are mutable, dict
keys (and set
members) need to be hashable, and hashing mutable objects is a bad idea because hash values should be computed on the basis of instance attributes.
In this answer, I will give some concrete examples, hopefully adding value on top of the existing answers. Every insight applies to the elements of the set
datastructure as well.
Example 1: hashing a mutable object where the hash value is based on a mutable characteristic of the object.
>>> class stupidlist(list):
... def __hash__(self):
... return len(self)
...
>>> stupid = stupidlist([1, 2, 3])
>>> d = {stupid: 0}
>>> stupid.append(4)
>>> stupid
[1, 2, 3, 4]
>>> d
{[1, 2, 3, 4]: 0}
>>> stupid in d
False
>>> stupid in d.keys()
False
>>> stupid in list(d.keys())
True
After mutating stupid
, it cannot be found in the dict any longer because the hash changed. Only a linear scan over the list of the dict's keys finds stupid
.
Example 2: ... but why not just a constant hash value?
>>> class stupidlist2(list):
... def __hash__(self):
... return id(self)
...
>>> stupidA = stupidlist2([1, 2, 3])
>>> stupidB = stupidlist2([1, 2, 3])
>>>
>>> stupidA == stupidB
True
>>> stupidA in {stupidB: 0}
False
That's not a good idea as well because equal objects should hash identically such that you can find them in a dict
or set
.
Example 3: ... ok, what about constant hashes across all instances?!
>>> class stupidlist3(list):
... def __hash__(self):
... return 1
...
>>> stupidC = stupidlist3([1, 2, 3])
>>> stupidD = stupidlist3([1, 2, 3])
>>> stupidE = stupidlist3([1, 2, 3, 4])
>>>
>>> stupidC in {stupidD: 0}
True
>>> stupidC in {stupidE: 0}
False
>>> d = {stupidC: 0}
>>> stupidC.append(5)
>>> stupidC in d
True
Things seem to work as expected, but think about what's happening: when all instances of your class produce the same hash value, you will have a hash collision whenever there are more than two instances as keys in a dict
or present in a set
.
Finding the right instance with my_dict[key]
or key in my_dict
(or item in my_set
) needs to perform as many equality checks as there are instances of stupidlist3
in the dict's keys (in the worst case). At this point, the purpose of the dictionary - O(1) lookup - is completely defeated. This is demonstrated in the following timings (done with IPython).
Some Timings for Example 3
>>> lists_list = [[i] for i in range(1000)]
>>> stupidlists_set = {stupidlist3([i]) for i in range(1000)}
>>> tuples_set = {(i,) for i in range(1000)}
>>> l = [999]
>>> s = stupidlist3([999])
>>> t = (999,)
>>>
>>> %timeit l in lists_list
25.5 µs ± 442 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit s in stupidlists_set
38.5 µs ± 61.2 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
>>> %timeit t in tuples_set
77.6 ns ± 1.5 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
As you can see, the membership test in our stupidlists_set
is even slower than a linear scan over the whole lists_list
, while you have the expected super fast lookup time (factor 500) in a set without loads of hash collisions.
TL; DR: you can use tuple(yourlist)
as dict
keys, because tuples are immutable and hashable.
Your awnser can be found here:
Why Lists Can't Be Dictionary Keys
Newcomers to Python often wonder why, while the language includes both a tuple and a list type, tuples are usable as a dictionary keys, while lists are not. This was a deliberate design decision, and can best be explained by first understanding how Python dictionaries work.
Source & more info: http://wiki.python.org/moin/DictionaryKeys
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