Почему делает Python 'для … в' работе по-другому над списком значений по сравнению со списком словарей?

Я задаюсь вопросом о некоторых деталях как для... в работах в Python.

Мое понимание for var in iterable на каждом повторении создает переменную, var, связанный с текущим значением повторяемых. Так, если Вы делаете for c in cows; c = cows[whatever], но изменение c в цикле не влияет на исходное значение. Однако это, кажется, работает по-другому при присвоении значения ключу словаря.

cows=[0,1,2,3,4,5]
for c in cows:
  c+=2

#cows is now the same - [0,1,2,3,4,5]

cows=[{'cow':0},{'cow':1},{'cow':2},{'cow':3},{'cow':4},{'cow':5}]
for c in cows:
  c['cow']+=2

# cows is now [{'cow': 2}, {'cow': 3}, {'cow': 4}, {'cow': 5}, {'cow': 6}, {'cow': 7}
#so, it's changed the original, unlike the previous example

Я вижу, что можно использовать, перечисляют, чтобы заставить первый пример работать также, но это - другая история, я предполагаю.

cows=[0,1,2,3,4,5]
for i,c in enumerate(cows):
  cows[i]+=1

# cows is now [1, 2, 3, 4, 5, 6]

Почему это влияет на исходные значения списка во втором примере, но не первом?

[править]

Спасибо за ответы. Я смотрел на это с точки зрения PHP, где можно использовать и символ в foreach, чтобы указать, воздействуете ли Вы на ссылку на или копию повторяемого. Я вижу теперь, когда реальной разницей является основная деталь как работы Python относительно неизменных объектов.

6
задан JAL 28 May 2010 в 16:38
поделиться

7 ответов

Это не имеет ничего общего с для ... в ... . Измените код с for c in cows: на c = cows[3] (и вычитайте следующую строку) в каждом примере и посмотрите эффект.

В вашем первом примере элементы списка - это объекты int; они неизменяемы. Во втором примере это объекты dict, которые являются изменяемыми.

4
ответ дан 8 December 2019 в 02:34
поделиться

Это помогает представить, что происходит со ссылкой, хранящейся в c на каждой итерации:

[ 0, 1, 2, 3, 4, 5 ]
  ^
  |
  c

c хранит ссылку, указывающую на первый элемент в списке. Когда вы выполняете c += 2 (то есть, c = c + 2, временной переменной c присваивается новое значение. Это новое значение равно 2, и c восстанавливается до этого нового значения. Исходный список остается в покое.

[ 0, 1, 2, 3, 4, 5 ]

  c -> 2

Теперь, в случае со словарем, вот к чему привязывается c во время первой итерации:

[ {'cow':0}, {'cow':1}, {'cow':2}, {'cow':3}, {'cow':4}, {'cow':5} ]
     ^
     |
     c

Здесь c указывает на объект словаря {'cow':0}. При выполнении c['cow'] += 2 (то есть, c['cow'] = c['cow'] + 2) изменяется сам объект словаря, так как c не восходит к несвязанному объекту. То есть, c по-прежнему указывает на тот первый объект словаря.

[ {'cow':2}, {'cow':1}, {'cow':2}, {'cow':3}, {'cow':4}, {'cow':5} ]
     ^
     |
     c
17
ответ дан 8 December 2019 в 02:34
поделиться

На самом деле это не ведет себя по-другому. Изменение переменной - это не то же самое, что изменение атрибута переменной. То же самое вы увидите в следующем примере:

a = 1
b = a
b = 2 

Здесь a по-прежнему 1. b было присвоено другое значение, и оно больше не равно a

a = {"hello": 1}
b = a
b["hello"] = 2 

Здесь a["hello"] возвращает 2 вместо 1. b все еще то же значение, потому что мы ничего не присвоили b, и поэтому b то же самое, что a. Мы изменили свойство ["hello"] из b на 2, а так как a и b - одна и та же переменная, то a["hello"] тоже 2

.
5
ответ дан 8 December 2019 в 02:34
поделиться

Во втором примере у вас есть список из объектов словаря. c ссылается на объект словаря, который модифицируется внутри области видимости цикла.

1
ответ дан 8 December 2019 в 02:34
поделиться

При присвоении имени, как в первом цикле, выполняется только повторное связывание имени. Присвоение элемента, как во втором цикле, изменяет существующий объект.

3
ответ дан 8 December 2019 в 02:34
поделиться

c - временная одноразовая переменная в обоих случаях. (Имейте в виду, что в Python все переменные являются просто ссылками, привязанными к объектам, которые они представляют, и могут быть повторно привязаны к различным объектам. Python в этом отношении более согласован, чем некоторые другие языки.)

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

В вашем примере dict каждая итерация обращается к dict, к которому временно привязан c , повторно привязывая один из членов этого dict к другому целому числу.

В обоих случаях c игнорируется в конце цикла, но, поскольку вы изменили структуру данных, отличную от c во втором случае, вы заметили изменения когда петля сделана.

4
ответ дан 8 December 2019 в 02:34
поделиться

Независимо от цикла, вы должны заметить, что:

some_var = some_object

связывает имя some_var с объектом some_object. Предыдущий объект (если он был), на который ссылается some_var, отвязывается.

some_var[some_index] = some_object

не связывает/развязывает some_var; это просто синтаксический сахар для следующего:

some_var.__setitem__(some_index, some_object)

Очевидно, что some_var по-прежнему указывает на тот же индексируемый (последовательность или отображение) объект, что и раньше, только измененный.

1
ответ дан 8 December 2019 в 02:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: