Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why did dict.get(key) work but not dict[key]?

I'm trying to group together the binary strings of certain numbers based on how many 1's there are in the string.

This doesn't work:

s = "0 1 3 7 8 9 11 15"
numbers = map(int, s.split())
binaries = [bin(x)[2:].rjust(4, '0') for x in numbers]

one_groups = dict.fromkeys(range(5), [])
for x in binaries:
    one_groups[x.count('1')] += [x]

The expected dictionary one_groups needs to be

{0: ['0000'], 
 1: ['0001', '1000'], 
 2: ['0011', '1001'], 
 3: ['0111', '1011'], 
 4: ['1111']}

But I get

{0: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 1: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 2: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 3: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111'], 
 4: ['0000', '0001', '0011', '0111', '1000', '1001', '1011', '1111']}

So far the only thing that has worked is if I use one_groups[x.count('1')] = one_groups.get(x.count('1')) + [x] instead of one_groups[x.count('1')] += [x]

But why is that so? If I recall correctly, isn't dict[key] supposed to return the value of that dictionary, similar to how dict.get(key) works? I've seen this thread Why dict.get(key) instead of dict[key]? but it didn't answer my question for this particular case, since I know for sure the program isn't meant to get the KeyError

I have also tried one_groups[x.count('1')].append(x) but this doesn't work either.

like image 267
SpectraXCD Avatar asked Nov 04 '19 14:11

SpectraXCD


People also ask

What does dict return if key doesn't exist?

If given key does not exists in dictionary, then it returns the passed default value argument. If given key does not exists in dictionary and Default value is also not provided, then it returns None.

What does dict keys () do in Python?

Python Dictionary keys() The keys() method extracts the keys of the dictionary and returns the list of keys as a view object.

How does dictionary get value from key?

You can use the get() method of the dictionary ( dict ) to get any default value without an error if the key does not exist. Specify the key as the first argument. The corresponding value is returned if the key exists, and None is returned if the key does not exist.

How do you check if a key doesn't exist in a dictionary Python?

You can check if a key exists in a dictionary using the keys() method and IN operator. The keys() method will return a list of keys available in the dictionary and IF , IN statement will check if the passed key is available in the list. If the key exists, it returns True else, it returns False .


3 Answers

The problem is mutability:

one_groups = dict.fromkeys(range(5), []) - this passes the same list as value to all keys. So if you change one value, you change them all.

It's basically the same as saying:

tmp = []
one_groups = dict.fromkeys(range(5), tmp)
del tmp

If you want to use a new list, you need to do it in a loop - either an explicit for loop or in a dict comprehension:

one_groups = {key: [] for key in range(5)}

This thing will "execute" [] (which equals to list()) for every key, thus making the values with different lists.


Why does get work? Because you explicitly take the current list, but + makes a new result list. And it doesn't matter whether it's one_groups[x.count('1')] = one_groups.get(x.count('1')) + [x] or one_groups[x.count('1')] = one_groups[x.count('1')] + [x] - what matters is that there's +.

I know how everybody says a+=b is just a=a+b, but the implementation may be different for optimisation - in case of lists, += is just .extend because we know we want our result in the current variable, so creating new list would be waste of memory.

like image 157
h4z3 Avatar answered Oct 21 '22 02:10

h4z3


The problem is using one_groups = dict.fromkeys(range(5), [])

(This passes the same list as value to all keys. So if you change one value, you change them all)


You can use this instead: one_groups = {i:[] for i in range(5)}

(This thing will "execute" [] (which equals to list()) for every key, thus making the values with different lists.)

like image 34
Hameda169 Avatar answered Oct 21 '22 02:10

Hameda169


This is the help on dict's fromkeys method.

Help on built-in function fromkeys:

fromkeys(iterable, value=None, /) method of builtins.type instance Create a new dictionary with keys from iterable and values set to value

That says that fromkeys will accept a value, and even it is a callable, it will evaluate it first, and then assign that value to all the dict keys.

Lists are mutable in Python, and so it will assign the same empty list reference and one change will affect them all.

Use defaultdict instead as so:

>>> from collections import defaultdict
>>> one_groups = defaultdict(list)
>>> for x in binaries:
      one_groups[x.count('1')] += [x]
>>> one_groups = dict(one_groups) # to stop default dict behavior

This will accept assignments to non-existing keys and values will default to empty lists (in this case).

like image 30
Fakher Mokadem Avatar answered Oct 21 '22 02:10

Fakher Mokadem