0

Code

Initially, I have some instances of classes stored in a dictionary, each with some initialized attribute.

class A:
    def __init__(self):
        self.foo = 'bar'

my_dict = {}

for i in range(3):
    my_dict[i] = A()

Now, I call a function on a dictionary value, my_dict[1], say, wanting to change the .foo attributes of all values in the dictionary, except that of my_dict[1], which I would like to change differently.

def outer(v):
    for k,v in my_dict.items():
        my_dict[k].foo = 'nada'
    v.foo = 'sand'

my_entry = my_dict[1]

outer(my_entry)

print(list((k,v.foo) for (k,v) in my_dict.items()))

However, the third line under outer(v) ends up changing my_dict[2].foo instead.

Problem

Consider the three lines in the function outer(v), and the output from the print(...).

  • Without first two lines, gives [(0, 'bar'), (1, 'sand'), (2, 'bar')] as expected.
  • Without third line, gives [(0, 'nada'), (1, 'nada'), (2, 'nada')] as expected.
  • However, with all three lines, gives [(0, 'nada'), (1, 'nada'), (2, 'sand')].

This suggests the variable v has been 'stuck' at index 2, following exit from for-loop. Why does Python do this?

Background

This problem caused a huge hold-up in a larger program I was making. This is my first attempt at asking a question on Stack Overflow by making a minimal, reproducible example. Any advice on how to make the question clearer or better is very much appreciated. Thank you.

Community
  • 1
  • 1
Benjamin Wang
  • 226
  • 1
  • 9
  • Because you assign to `v` in your for-loop, i.e. `for k,v in my_dict.items():`. Now, `v` in the last line of your function will refer to whatever `v` is on the last iteration of your for-loop. I'm not sure what you are asking. Why does Python do this? *You* did this. But assigning to `v`, essentially, ignoring whatever value you passed in to the parameter `v`. I'm confused, because you already seem to understand that when you say shadowed. – juanpa.arrivillaga Dec 12 '19 at 13:30
  • Perhaps, were you expecting loops to have their own scope? – juanpa.arrivillaga Dec 12 '19 at 13:35
  • 2
    This isn't shadowing, because Python doesn't have block scope. You just reassigned the variable, so its old value is lost. – kaya3 Dec 12 '19 at 13:43
  • It jumped right into my eye that you didn't just `for k in my_dict:` when not using the value inside the loop, so I guess the error you get being a side-effect of that is just karma. ;) – Jeronimo Dec 12 '19 at 14:35
  • @juanpa.arrivillaga Thank you. I was expecting loops to have their own scope. – Benjamin Wang Dec 12 '19 at 14:52
  • @kaya3 Thank you. This completely answers my question. – Benjamin Wang Dec 12 '19 at 14:52
  • @Jeronimo Ah okay. In my original code, I had a check condition (if clause) that depends on v. Hence I used this. I figured it would be clearer. Thank you :D – Benjamin Wang Dec 12 '19 at 14:57

0 Answers0