Home > Software engineering >  Update Python dictionary while looping
Update Python dictionary while looping

Time:10-26

I was trying to iterate over a list of values, craft a dictionary to save each value in a structured way, and then append the dictionary to a new list of results, but I found an unexpected behavior.

Below is an example:

values_list = [1,2,3]

# Basic dict
result_dict = {
    'my_value': ''
  }

# Iterate, craft a dictionary, and append result

dicts_list = []

for value in values_list:
  result_dict.update({'my_value': value})
  dicts_list.append(result_dict)

print(dicts_list)

As you can see, first I create a basic dictionary, then I'm iterating over the list of values and updating the dictionary, at the end I'm appending the crafted dictionary to a separate list of results (dicts_list).

As a result I was expecting:

[{'my_value': 1}, {'my_value': 2}, {'my_value': 3}]

but instead I was getting:

[{'my_value': 3}, {'my_value': 3}, {'my_value': 3}]

It looks like every iteration is not only updating the basic dictionary – which is expected – but also the dictionaries already appended to the list of results on the previous iteration.

To fix the issue, I nested the basic dictionary under the for loop:

values_list = [1,2,3]

# Iterate, craft a dictionary, and append result

dicts_list = []

for value in values_list:
  result_dict = {'my_value': ''}
  result_dict.update({'my_value': value})
  dicts_list.append(result_dict)

print(dicts_list)

Can anyone explain what is wrong with the first approach? How is the loop causing the list of appended dictionaries to be updated?

Thanks for any advice! :)

Franz

CodePudding user response:

as explained in the comment you're appending the same dictionary in each iteration because update() modifies the result_dict rather than returning a copy; so the only change you need to do is to append a copy of the crafted dictionary, e.g.:

values_list = [1,2,3]

# Basic dict
result_dict = {
    'my_value': ''
  }

# Iterate, craft a dictionary, and append result

dicts_list = []

for value in values_list:
  result_dict.update({'my_value': value})
  dicts_list.append(dict(result_dict))  # <--- this is the only change

print(dicts_list)

CodePudding user response:

Can anyone explain what is wrong with the first approach? How is the loop causing the list of appended dictionaries to be updated?

It is NOT the loop causing the effect you have experienced.

It is the way you think what appending to a list does in case when the appended something is a mutable object like a list, dictionary, set, ... and not a value like a float, integer, string, ... .

To gain deeper understanding of Python changing generally your expectations for an outcome when dealing with Python mutable objects study the following code and read the in code provided comments explaining the 'behind the scenes' of what you have experienced as unexpected behavior:

# ----------------------------------------------------------------------
# Behavior of not mutable Python objects like integer values or strings 
# (which don't have ITEMs you can modify):  
a = 1
b = a  
# 'b' is now an identifier with which you can get the value stored in 'a' 
#     at the time of executing b = a 
print(a,b) # gives: 1 1
a = 2    # assignment to 'a' has no effect on value got when using 'b'
#          to obtain a value, therefore: 
print(a,b) # gives: 2 1
#        you CAN'T assign a value to the ITEM of 'a' as an integer value
#        does not have ITEMs. 

# ----------------------------------------------------------------------
# Behavior of mutable Python objects like lists or dictionaries (where 
# you can change their ITEMs): 
L = [1]
M = L   
# 'M' is now an identifier with which, assuming that in between there 
#     will be no new assignment to 'L', you can get values stored in L 
#     at any time after executing M = L getting then the current values 
#     stored in L at the time of using M for getting the values. 
print(L,M) # gives: [1] [1]
L[0] = 2 # assignment to an ITEM of 'L' does not change the "value" of
#        of 'L' being an 'address' of a list object stored in memory 
#        which is at the same time also the "value" of 'M', therefore:  
print(L,M) # gives: [2] [2]
L = [1]  # assignment to 'L' changes the "value" of L. The previously 
#        in 'L' stored "value" will be lost if not the fact that this
#        "value' is still the "value' of 'M' being 'address' of a list
#        object [2] storing the integer '2'. The assignment to L does 
#        not have any effect on M, therefore: 
print(L,M) # gives: [1] [2]
  • Related