Reputation: 9
I would like to change the values of a list that is the value of a dictionary. When I code this in the pythonic way it does not work, but it does work when I use classical indexing of the list. To make clear what I mean I wrote a small demo. I want to increase all values lower than 4 in a list by one.
A = [1, 2, 3, 4, 5]
x = {"a": [1, 2, 3, 4, 5]}
print("example 1, this works")
A = [a+1 if a < 4 else a for a in A]
print(A)
print("example 2, this not")
for v in x.values():
v = [a + 1 if a < 4 else a for a in v]
print(x)
print("example 3, this not either")
for v in x.values():
for a in v:
a = a+1 if a < 4 else a
print(x)
print("example 4, but this does")
for v in x.values():
for i in range(len(v)):
if v[i] < 4: v[i] += 1
print(x)
Output:
example 1, this works
[2, 3, 4, 4, 5]
example 2, this not
{'a': [1, 2, 3, 4, 5]}
example 3, this not either
{'a': [1, 2, 3, 4, 5]}
example 4, but this does
{'a': [2, 3, 4, 4, 5]}
Two things are puzzling: 1. A list is treated differently depending on whether it is a dict value or not. 2. Changing a list value works or not depending on which looping technique you apply.
Is there a rationale that is beyond me? (Could be) If yes, what is it?
Upvotes: 0
Views: 84
Reputation: 808
Interesting question. I think the crux of this is that the dict method .values() returns a view object not a direct reference to the array that is the value for that key.
So you can't change the dictionaries list value by changing the list that is returned by .values(). So you will need to directly access the dictionary element. One way to do this would be using .items() instead:
x = {"a": [1, 2, 3, 4, 5]}
for key, value in x.items():
x[key] = [a + 1 if a < 4 else a for a in value]
See also the comments in this question for more discussion.
Upvotes: 0
Reputation: 140168
in this code:
for v in x.values():
v = [a + 1 if a < 4 else a for a in v]
you're creating a new reference for v
, which is lost at the next iteration, so it doesn't reflect in the dictionary. I would do this:
for v in x.values():
v[:] = [a + 1 if a < 4 else a for a in v]
so the reference doesn't change and the dict is updated. Another way of doing it is accessing the values through the keys. You're creating another reference, but it's stored in the dictionary:
for k in x:
x[k] = [a + 1 if a < 4 else a for a in v]
The example (4) of your question works for the same (inverse) reason: you're working on the reference of v
contained in the dictionary without modifiying it, so changes are reflected in the dictionary.
Upvotes: 1