Why can't I use a list as a dict key in python?

I'm a little confused about what may / may not be used as a key for python dictate.

dicked = {} dicked[None] = 'foo' # None ok dicked[(1,3)] = 'baz' # tuple ok import sys dicked[sys] = 'bar' # wow, even a module is ok ! dicked[(1,[3])] = 'qux' # oops, not allowed 

So, a tuple is an immutable type, but if I hide it inside, then it cannot be a key. Could I just hide the list inside the module?

I had some vague idea that the key should be "hashed", but I will simply admit my ignorance regarding the technical details; I do not know what is going on here. What will go wrong if you try to use lists as keys, and a hash like, say, their memory location?

+71
python dictionary list tuples hashable
Aug 31 2018-11-11T00:
source share
9 answers

The Python wiki has a good article on this topic: Why lists cannot be keywords for a dictionary . As explained there:

What will go wrong if you try to use lists as keys, and a hash like, say, their memory location?

This can be done without violating any requirements, but this leads to unexpected behavior. Lists are usually processed as if their value was obtained from their content values, for example, when checking (in) equality. Many might, of course, expect that you can use any list [1, 2] to get the same key where you would need to maintain exactly the same list object. But the search by values ​​is broken as soon as the list used as a key changes, and searching by identifier requires that you stick to exactly the same list that is not required for any other operation of the general list (at least I cannot to think about).

Other objects, such as modules and object , are in any case significantly different from their object identifier (when was the last time you had two separate module objects called sys ?), And in any case they are compared. Therefore, it is less surprising or even expected that they, when used as key keys, are compared by identity in this case.

+21
Aug 31 2018-11-11T00:
source share

Why can't I use a list as a dict key in python?

 >>> d = {repr([1,2,3]): 'value'} {'[1, 2, 3]': 'value'} 

(for anyone who stumbles on this question, looking for a way around it)

as others explain here, really, you cannot. However, you can use its string representation if you really want to use your list.

+21
Aug 31 '11 at 13:55
source share

The problem is that tuples are immutable, but lists are not. Consider the following

 d = {} li = [1,2,3] d[li] = 5 li.append(4) 

What should return d[li] ? Is this the same list? What about d[[1,2,3]] ? It has the same meaning, but a different list?

Ultimately, there is no satisfactory answer. For example, if the only key that works is the source key, then if you do not have a link to that key, you can never access the value again. Using any other permitted key, you can create a key without reference to the original.

If both of my suggestions work, then you have very different keys that return the same value, which is somewhat surprising. If only the original content works, then your key will go wrong quickly, since the lists will be changed.

+9
Aug 31 '11 at 13:32
source share

Here's the answer http://wiki.python.org/moin/DictionaryKeys

What will go wrong if you try to use lists as keys, and a hash like, say, their memory location?

Searching for different lists with the same content will lead to different results, although comparing lists with the same content means their equivalent.

How to use list literal in dictionary search?

+7
Aug 31 '11 at 1:31 on
source share

So far you can change the list into a tuple and then use it as keys.

 d = {tuple([1,2,3]): 'value'} 
+5
Aug 02 '18 at 18:44
source share

Your awning can be found here:

Why lists cannot be keys for a word

Python newbies often wonder why, while the language includes both a tuple and a list type, tuples can be used as dictionary keys, while there are no lists. This was a deliberate design decision, and is best explained by a first understanding of how Python dictionaries work.

Source and additional information: http://wiki.python.org/moin/DictionaryKeys

+2
Aug 31 2018-11-11T00:
source share

The simple answer to your question is that the hash of the method that is required for any object that wants to use as a key in the dictionary is not implemented in the list of classes. However, the reason the hash is not implemented in the same way as, say, in the tuple class (based on the contents of the container) is because the list is mutable, so editing the list will require recalculating the hash, which may mean that the list is now in in the wrong bucket in the sub hash table. Note that since you cannot change the tuple (immutable), it does not encounter this problem.

As a note, the actual implementation of the search for dictobjects is based on the D algorithm from Knuth Vol. 3, ch. 6.4. If this book is available to you, it is worth reading, in addition, if you are really, really interested, you can take a look at the comments of the developers on the actual implementation of dictobject here. It details how this works. There is also a python lecture on the implementation of dictionaries that may interest you. They go through the definition of the key and what the hash is in the first few minutes.

+1
Aug 31 2018-11-11T00:
source share

Since lists are mutable, dict keys (and set members) must be hashed, and hashing mutable objects is a bad idea because the values ​​of the hash function must be calculated based on the attributes of the instance.

In this answer, I will give some specific examples that hopefully add value over existing answers. Each understanding also applies to set elements.

Example 1 : hashing a mutable object, where the value of the hash function is based on the 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 Dictionary anymore because the hash has changed. Only a linear scan over the dict key list 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 

This is also not a good idea, because identical objects must be hashed the same way so that they can be found in dict or set .

Example 3 : ... well, what about persistent hashes in all cases ?!

 >>> 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 

Everything seems to work as expected, but think about what happens: when all instances of your class produce the same hash value, you will have a hash value conflict if there are more than two in the dict or set instances as keys.

To find the required instance using my_dict[key] or key in my_dict (or item in my_set ), you need to perform as many equality checks as there are stupidlist3 examples in dict keys (in the worst case). At the moment, the goal of the dictionary - search O (1) - is completely defeated. This is demonstrated in the following cases (done with IPython).

Some terms 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 even slower than a linear scan of the entire lists_list , while you have the expected ultra-fast search time (coefficient 500) in the set without a lot of hash collisions.




TL; DR: you can use tuple(yourlist) as dict keys, because tuples are immutable and hashed.

+1
Oct 25 '18 at 22:18
source share

According to Python 2.7.2 documentation:

A hashable object if it has a hash value that never changes during its life cycle (it needs a hash method ()) and can be compared to other objects (this requires eq () or cmp ()). Hashable objects that are compared equal must have the same hash value.

Hashability makes an object usable as a dictionary key and a member set because these data structures use a hash value internally.

All unused Pythons built-in objects are hashable, and no mutable containers (like lists or dictionaries). Objects that are instances of custom classes are hashed by default; They all compare unevenly, and their hash value is their id ().

A tuple is immutable in the sense that you cannot add, delete, or replace its elements, but the elements themselves can be mutable. The hash value of the list depends on the hash values ​​of its elements, and therefore it changes when the elements change.

Using id for list hashes will mean that all lists are compared in different ways, which would be surprising and inconvenient.

0
Aug 31 '11 at 13:44
source share



All Articles