Почему затенение цикла for в Python нарушает внешнюю функцию?

Код

Изначально у меня есть несколько экземпляров классов, хранящихся в словаре, каждый с некоторым инициализированным атрибутом.

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

my_dict = {}

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

Теперь я вызываю функцию для значения словаря, скажем, my_dict[1], желая изменить атрибуты .foo всех значений в словаре, кроме атрибута my_dict[1], который я хотел бы изменить по-другому.

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()))

Однако третья строка под outer(v) вместо этого меняет my_dict[2].foo.

Проблема

Рассмотрим три строки в функции outer(v) и вывод функции print(...).

  • Без первых двух строк дает [(0, 'bar'), (1, 'sand'), (2, 'bar')], как и ожидалось.
  • Без третьей строки дает [(0, 'nada'), (1, 'nada'), (2, 'nada')], как и ожидалось.
  • Однако со всеми тремя строками дает [(0, 'nada'), (1, 'nada'), (2, 'sand')].

Это говорит о том, что переменная v «застряла» на индексе 2 после выхода из цикла for. Почему Python делает это?

Фон

Эта проблема вызвала огромную задержку в большой программе, которую я делал. Это моя первая попытка задать вопрос о переполнении стека на минимальном воспроизводимом примере. Любые советы о том, как сделать вопрос яснее или лучше, очень ценятся. Спасибо.


person Benjamin Wang    schedule 12.12.2019    source источник
comment
Поскольку вы назначаете v в цикле for, то есть for k,v in my_dict.items():. Теперь v в последней строке вашей функции будет ссылаться на то, что v находится на последней итерации вашего цикла for. Я не уверен, что вы спрашиваете. Почему Python делает это? Вы сделали это. Но назначение v, по сути, игнорирует любое значение, которое вы передали параметру v. Я в замешательстве, потому что вы, кажется, уже понимаете это, когда говорите «затененный».   -  person juanpa.arrivillaga    schedule 12.12.2019
comment
Возможно, вы ожидали, что циклы будут иметь свою область видимости?   -  person juanpa.arrivillaga    schedule 12.12.2019
comment
Это не затенение, потому что у Python нет блочной области. Вы только что переназначили переменную, поэтому ее старое значение потеряно.   -  person kaya3    schedule 12.12.2019
comment
Мне сразу бросилось в глаза, что вы не просто for k in my_dict:, когда не используете значение внутри цикла, поэтому я думаю, что ошибка, которую вы получаете, является побочным эффектом, это просто карма. ;)   -  person Jeronimo    schedule 12.12.2019
comment
@juanpa.arrivillaga Спасибо. Я ожидал, что циклы будут иметь свою собственную область видимости.   -  person Benjamin Wang    schedule 12.12.2019
comment
@kaya3 Спасибо. Это полностью отвечает на мой вопрос.   -  person Benjamin Wang    schedule 12.12.2019
comment
@Jeronimo А, хорошо. В моем исходном коде у меня было условие проверки (предложение if), которое зависит от v. Поэтому я использовал это. Я думал, так будет понятнее. Спасибо :D   -  person Benjamin Wang    schedule 12.12.2019