Strange behaviou of dict python [duplicate] - python

This question already has answers here:
Python dict.fromkeys return same id element
(2 answers)
Closed 3 years ago.
I've a dict code snippets which is not behaving as expected
a = {"d1":{"a":1,"b":2,"c":4},"d2":{"a":1,"b":2,"c":4},"d3":{"a":1,"b":2,"c":4}}
b = {"d1":{"a":1,"b":0},"d2":{"a":0,"c":4},"d3":{"a":1,"b":2,"c":4}}
c = dict.fromkeys(a.keys(),{})
print(c)
for doc in b.keys():
for word in b[doc].keys():
c[doc][word] = a[doc][word]*b[doc][word]
print(c)
output is:
{'d1': {}, 'd2': {}, 'd3': {}}
{'d1': {'a': 1, 'b': 4, 'c': 16}, 'd2': {'a': 1, 'b': 4, 'c': 16}, 'd3': {'a': 1, 'b': 4, 'c': 16}}
instead of:
{'d1': {}, 'd2': {}, 'd3': {}}
{'d1': {'a': 1, 'b': 0}, 'd2': {'a': 0, 'c': 16}, 'd3': {'a': 1, 'b': 4, 'c': 16}}
I very confused now any insights would be helpful.

The problem is because you are using a mutable object as the second argument for fromkeys.
This is much clearer here:
d = dict.fromkeys(['a', 'b'], [])
d['a'].append(1)
print(d)
Outputs
{'a': [1], 'b': [1]}

Made a modification to your for loop :
for doc in b.keys():
for word in b[doc].keys():
if doc not in c:
c[doc]={}
c[doc][word] = a[doc][word]*b[doc][word]
print(c)
#{'d1': {'a': 1, 'b': 0}, 'd2': {'a': 0, 'c': 16}, 'd3': {'a': 1, 'b': 4, 'c': 16}}

Use a dictionary comprehension to create c instead:
c = {k: {} for k in a.keys()}
for doc in b.keys():
for word in b[doc].keys():
c[doc][word] = a[doc][word]*b[doc][word]
print(c)
# {'d1': {'a': 1, 'b': 0}, 'd2': {'a': 0, 'c': 16}, 'd3': {'a': 1, 'b': 4, 'c': 16}}
Notice the difference when you use fromkeys vs dictionary comprehension:
c = dict.fromkeys(a.keys(),{})
print([id(o) for o in c.values()])
# [53649152, 53649152, 53649152]
# same object reference id!
c = {k: {} for k in a.keys()}
print([id(o) for o in c.values()])
# [53710208, 53649104, 14445232]
# each object has different reference id

Related

Python. How to merge two dictionaries with the same keys?

I have two dicts:
a = {'a': 1, 'b': 2, 'c': 3}
b = {'a': 2, 'd': 4, 'c': 5}
and i want to get:
{'a': 2, 'b': 2, 'c': 5}
i used {**a, **b} but it return:
{'a': 2, 'b': 2, 'c': 5, 'd': 4}
Help me please exclude keys from b which not in a with the simplest and fastest way.
i have python 3.7
You have to filter the elements of the second dict first in order to not add any new elements. I got two possible solutions:
a = {'a': 1, 'b': 2, 'c': 3}
b = {'a': 2, 'd': 4, 'c': 5}
for k,v in b.items():
if (k in a.keys()):
a[k] = v
print(a)
a = {'a': 1, 'b': 2, 'c': 3}
b = {'a': 2, 'd': 4, 'c': 5}
a.update([(k,v) for k, v in b.items() if k in a.keys()])
print(a)
Output for both:
{'a': 2, 'b': 2, 'c': 5}
I think a comprehension is easy enough:
{ i : (b[i] if i in b else a[i]) for i in a }

list of dictionary compare first value of first dict with first value of next dict

I have a list of dictionaries, for example:
l = [{"a":1, "b":2, "c":3}, {"a":1, "b":2, "c":4}, {"a":1, "b":7, "c":4}, {"a":2, "b":7, "c":4}]
I need to create a nested dictionary if value of "a" are equal.
I have tried:
l2 = [l[i] for i in range(len(l)-1) if l[i].get('a') == l[i+1].get('a')]
d = {"element"+ str(index): x for index, x in enumerate(l2, start=1)}
But in the output, I'm getting it skips one element:
{'element1': {'a': 1, 'b': 2, 'c': 3}, 'element2': {'a': 1, 'b': 2, 'c': 4}}
Expected output:
{'element1': {'a': 1, 'b': 2, 'c': 3}, 'element2': {'a': 1, 'b': 2, 'c': 4}, 'element3': {"a":1, "b":7, "c":4}}
Could someone please help me, what am I doing wrong?
Try this:
out = {f'element{i + 1}': j for i, j in enumerate(l) if any(j['a'] == k['a'] for k in l[:i] + l[i+1:])}
Output:
{'element1': {'a': 1, 'b': 2, 'c': 3}, 'element2': {'a': 1, 'b': 2, 'c': 4}, 'element3': {'a': 1, 'b': 7, 'c': 4}}

How to change specific values in dictionary regardless of the keys in python?

How can I change the values in a dictionary in python regardless of the keys? If we take the following dictionary for our example:
d = {'a': 1, 'b': 2, 'c': 0, 'd': 2, 'e': 1}
Now I want to manipulate the dictionary in the way that all the values 2 will be changed into 3 so the output would be:
d = {'a': 1, 'b': 3, 'c': 0, 'd': 3, 'e': 1}
I am sure the problem is very basic, but I somehow haven't managed to find the answer online.
Thank you in advance.
In [338]: d = {'a': 1, 'b': 2, 'c': 0, 'd': 2, 'e': 1}
In [339]: for k,v in d.items():
...: if v == 2:
...: d[k] = 3
...:
In [340]: d
Out[340]: {'a': 1, 'b': 3, 'c': 0, 'd': 3, 'e': 1}

dict() in for loop - different behavior

I am trying to update values for a dict() key dynamically with a for loop.
def update_dict():
f = []
for i, j in enumerate(list_range):
test_dict.update({'a': i})
j['c'] = test_dict
print(j)
f.append(j)
print(f)
test_dict = dict({'a': 1})
list_range = [{'b': i} for i in range(0, 5)]
update_dict()
Even print(j) gives iterating value (0,1,2,3,4), somehow the last dict is getting overwritten all over the list and giving wrong output (4,4,4,4,4).
Expected Output,
[{'b': 0, 'c': {'a': 0}}, {'b': 1, 'c': {'a': 1}}, {'b': 2, 'c': {'a': 2}}, {'b': 3, 'c': {'a': 3}}, {'b': 4, 'c': {'a': 4}}]
Output obtained,
[{'b': 0, 'c': {'a': 4}}, {'b': 1, 'c': {'a': 4}}, {'b': 2, 'c': {'a': 4}}, {'b': 3, 'c': {'a': 4}}, {'b': 4, 'c': {'a': 4}}]
I need to understand how the dictionaries are getting overwritten and what could be the best solution to avoid this?
Thanks in advance!
P.S. : please avoid suggesting list or dict comprehension method as bare answer as i am aware of them and the only purpose of this question is to understand the wrong behavior of dict().
The reason of such behaviour is that all references in list points to the same dict. Line j['c'] = test_dict doesn't create copy of dictionary, but just make j['c'] refer to test_dict. To get expected result you need change this line to:
j['c'] = test_dict.copy(). It will make deep copy of test_dict and assign it to j['c'].
You try to add values to same dictionary every time in the loop and as loop progresses, you keep replacing the values.
You need to define dictionary in every iteration to create separate references of the dictionary:
def update_dict():
f = []
for i, j in enumerate(list_range):
test_dict = {'a': i}
j['c'] = test_dict
f.append(j)
print(f)
list_range = [{'b': i} for i in range(0, 5)]
update_dict()
# [{'b': 0, 'c': {'a': 0}},
# {'b': 1, 'c': {'a': 1}},
# {'b': 2, 'c': {'a': 2}},
# {'b': 3, 'c': {'a': 3}},
# {'b': 4, 'c': {'a': 4}}]
A simpler solution could be to iterate through list_range and create c using the values from b
lista = [{'b': i } for i in range(0, 5)]
for i in lista:
i['c'] = {'a': i['b']}
# [{'b': 0, 'c': {'a': 0}}, {'b': 1, 'c': {'a': 1}}, {'b': 2, 'c': {'a': 2}}, {'b': 3, 'c': {'a': 3}}, {'b': 4, 'c': {'a': 4}}]
def update_dict():
f = []
for i, j in enumerate(list_range):
j['c'] = {'a': i}
print(j)
f.append(j)
return f
list_range = [{'b': i} for i in range(0, 5)]
print(update_dict())
#output
{'b': 0, 'c': {'a': 0}}
{'b': 1, 'c': {'a': 1}}
{'b': 2, 'c': {'a': 2}}
{'b': 3, 'c': {'a': 3}}
{'b': 4, 'c': {'a': 4}}

Explode a dict - Get all combinations of the values in a dictionary

I want to get all combinations of the values in a dictionary as multiple dictionaries (each containing every key of the original but only one value of the original values). Say I want to parametrize a function call with:
kwargs = {'a': [1, 2, 3], 'b': [1, 2, 3]}
How do I get a list of all the combinations like this:
combinations = [{'a': 1, 'b': 1}, {'a': 1, 'b': 2}, {'a': 1, 'b': 3},
{'a': 2, 'b': 1}, {'a': 2, 'b': 2}, {'a': 2, 'b': 3},
{'a': 3, 'b': 1}, {'a': 3, 'b': 2}, {'a': 3, 'b': 3}]
There can be an arbitary amount of keys in the original kwargs and each value is garantueed to be an iterable but the number of values is not fixed.
If possible: the final combinations should be a generator (not a list).
You can flatten the kwargs to something like this
>>> kwargs = {'a': [1, 2, 3], 'b': [1, 2, 3]}
>>> flat = [[(k, v) for v in vs] for k, vs in kwargs.items()]
>>> flat
[[('b', 1), ('b', 2), ('b', 3)], [('a', 1), ('a', 2), ('a', 3)]]
Then, you can use itertools.product like this
>>> from itertools import product
>>> [dict(items) for items in product(*flat)]
[{'a': 1, 'b': 1},
{'a': 2, 'b': 1},
{'a': 3, 'b': 1},
{'a': 1, 'b': 2},
{'a': 2, 'b': 2},
{'a': 3, 'b': 2},
{'a': 1, 'b': 3},
{'a': 2, 'b': 3},
{'a': 3, 'b': 3}]
itertools.product actually returns an iterator. So you can get the values on demand and build your dictionaries. Or you can use map, which also returns an iterator.
>>> for item in map(dict, product(*flat)):
... print(item)
...
...
{'b': 1, 'a': 1}
{'b': 1, 'a': 2}
{'b': 1, 'a': 3}
{'b': 2, 'a': 1}
{'b': 2, 'a': 2}
{'b': 2, 'a': 3}
{'b': 3, 'a': 1}
{'b': 3, 'a': 2}
{'b': 3, 'a': 3}
Just another way, building the value tuples first and then combining with keys afterwards (pretty much the opposite of #thefourtheye's way :-).
>>> combinations = (dict(zip(kwargs, vs)) for vs in product(*kwargs.values()))
>>> for c in combinations:
print(c)
{'a': 1, 'b': 1}
{'a': 1, 'b': 2}
{'a': 1, 'b': 3}
{'a': 2, 'b': 1}
{'a': 2, 'b': 2}
{'a': 2, 'b': 3}
{'a': 3, 'b': 1}
{'a': 3, 'b': 2}
{'a': 3, 'b': 3}

Categories

Resources