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.

7
  • 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. Commented Dec 12, 2019 at 13:30
  • Perhaps, were you expecting loops to have their own scope? Commented Dec 12, 2019 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. Commented Dec 12, 2019 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. ;) Commented Dec 12, 2019 at 14:35
  • @juanpa.arrivillaga Thank you. I was expecting loops to have their own scope. Commented Dec 12, 2019 at 14:52

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.