Я задаюсь вопросом о некоторых деталях как для... в работах в 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 относительно неизменных объектов.
Это не имеет ничего общего с для ... в ...
. Измените код с for c in cows:
на c = cows[3]
(и вычитайте следующую строку) в каждом примере и посмотрите эффект.
В вашем первом примере элементы списка - это объекты int; они неизменяемы. Во втором примере это объекты dict, которые являются изменяемыми.
Это помогает представить, что происходит со ссылкой, хранящейся в 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
На самом деле это не ведет себя по-другому. Изменение переменной - это не то же самое, что изменение атрибута переменной. То же самое вы увидите в следующем примере:
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
Во втором примере у вас есть список из объектов словаря. c
ссылается на объект словаря, который модифицируется внутри области видимости цикла.
При присвоении имени, как в первом цикле, выполняется только повторное связывание имени. Присвоение элемента, как во втором цикле, изменяет существующий объект.
c
- временная одноразовая переменная в обоих случаях. (Имейте в виду, что в Python все переменные являются просто ссылками, привязанными к объектам, которые они представляют, и могут быть повторно привязаны к различным объектам. Python в этом отношении более согласован, чем некоторые другие языки.)
В вашем примере со списком, каждая итерация выполняет повторную привязку c
от одного целого числа к другому, оставляя исходный список без изменений.
В вашем примере dict каждая итерация обращается к dict, к которому временно привязан c
, повторно привязывая один из членов этого dict к другому целому числу.
В обоих случаях c
игнорируется в конце цикла, но, поскольку вы изменили структуру данных, отличную от c
во втором случае, вы заметили изменения когда петля сделана.
Независимо от цикла, вы должны заметить, что:
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
по-прежнему указывает на тот же индексируемый (последовательность или отображение) объект, что и раньше, только измененный.