Ian
Ian

Reputation: 109

Dictionary give the values to the certain key, but the others get the same values

I want to create a dictionary to record the accurancy when I run the program.There are some problem when I give the values to the certain key

Here, I create a dictionary to record the accurancy when it trains or validate

acc = dict.fromkeys(['train', 'val'], dict.fromkeys(['pa', 'iou', 'dice'], 0))

The original dictionary looks like this

{'train': {'pa': 0, 'iou': 0, 'dice': 0}, 'val': {'pa': 0, 'iou': 0, 'dice': 0}}

Now, I give the some values for testing

acc['train']['pa'] = 0.9
acc['train']['iou'] = 0.8
acc['train']['dice'] = 0.7

The problems is all the values in key val becomes the same

{'train': {'pa': 0.9, 'iou': 0.8, 'dice': 0.7}, 'val': {'pa': 0.9, 'iou': 0.8, 'dice': 0.7}}

Upvotes: 1

Views: 84

Answers (2)

Sriteja Sugoor
Sriteja Sugoor

Reputation: 604

If the provided value is a mutable object (whose value can be modified) like list, dictionary, etc., when the mutable object is modified, each element of the sequence also gets updated. This is because, each element is assigned a reference to the same object (points to the same object in the memory).

In your problem, dict.fromkeys(['pa', 'iou', 'dice'], 0) returns a dictionary which is mutable type, so each element is assigned to the same object.

To avoid this issue, you can use dictionary comprehension.

from copy import copy
# keys
keys = ['train', 'val']
value = dict.fromkeys(['pa', 'iou', 'dice'], 0 )

acc = { key : copy(value) for key in keys }

print(acc)

# updating the value
acc['train']['pa'] = 2 
print(acc)

Source: https://www.programiz.com/python-programming/methods/dictionary/fromkeys

Upvotes: 1

timgeb
timgeb

Reputation: 78690

That's because dict.fromkeys(['pa', 'iou', 'dice'], 0) is called exactly once, not once for each key in acc.

>>> acc['train'] is acc['val']                                                  
True

The dictionaries are the same object in memory, you just have two ways to access it - via acc['train'] and acc['val'].

You could use a defaultdict with a function that creates a new dictionary on demand:

>>> from collections import defaultdict                                         
>>> d = defaultdict(lambda: {'pa': 0, 'iou': 0, 'dice': 0})                     
>>> d['train']                                                                  
{'pa': 0, 'iou': 0, 'dice': 0}
>>> d['val']                                                                    
{'pa': 0, 'iou': 0, 'dice': 0}
>>> d['train']['pa'] = 1                                                        
>>> d['train']                                                                  
{'pa': 1, 'iou': 0, 'dice': 0}
>>> d['val']                                                                    
{'pa': 0, 'iou': 0, 'dice': 0}

However, be aware that this creates a new dictionary each time you access a new key:

>>> d['foo']                                                                    
{'pa': 0, 'iou': 0, 'dice': 0}

If you don't want that, create a dictionary for each key manually with a dict comprehension:

>>> keys = ['train', 'val']                                                     
>>> d = {k:{'pa': 0, 'iou': 0, 'dice': 0} for k in keys}
>>> d['train']['pa'] = 1                                                        
>>> d['train']                                                                  
{'pa': 1, 'iou': 0, 'dice': 0}
>>> d['val']                                                                    
{'pa': 0, 'iou': 0, 'dice': 0}

Upvotes: 1

Related Questions