Какие эффекты оказывает объявление переменной за a для l oop (для добавления в список) на последующие манипуляции с элементами в списке? - PullRequest
1 голос
/ 28 марта 2020

Я сейчас слежу за "Python Cra sh Course" от Eri c Matthes.

Я добавляю 30 новых предметов в список, используя для l oop. Все предметы являются словарями. Затем я пытаюсь обновить первые несколько элементов списка, используя следующий код (фактический код, размещенный в конце) -

for item in items[0:3]:
    if item['someKey'] == 'someValue':
        item['someKey'] = 'someOtherValue'
        item['someOtherKey'] = 'someDifferentValue'

Итак, поскольку я меняю только первые 3 элемента, он должен меняться только первым 3 предмета. Но это меняет все элементы в списке, если я добавляю в список, используя переменную, объявленную вне для l oop, при добавлении элементов в первую очередь .

#Case-1
items = []
dictionary = {'someKey': 'someValue', 'someOtherKey': 'someOtherValue'}
for item in range(30):
    items.append(dictionary)

Если я запустите этот код, а затем запустите для l oop, чтобы обновить некоторые элементы, затем все элементы в списке будут изменены. Разделение списка с помощью [0:3] не работает!

#Case-2
items = []
for item in range(30):
    dictionary = {'someKey': 'someValue', 'someOtherKey': 'someOtherValue'}
    items.append(dictionary)

Таким образом, в этом случае процесс обновления работает, как и ожидалось. Для l oop просто обновляются только первые 3 элемента. Почему это происходит? Не имею представления! Список создается в обоих случаях просто отлично . Только при изменении уже созданного списка поведение отличается .


Вот фактический код -

#Case-1
aliens = []
newAlien = {'color': 'green', 'speed': 'slow', 'points': 5}

for alienNumber in range(30):
   aliens.append(newAlien)

print(aliens) #Prints the whole list, showing adding dicts went just fine

for alien in aliens[0:3]: #intending change for only first 3 items
    if alien['color'] == 'green':
        alien['color'] = 'yellow'
        alien['speed'] = 'medium'
        alien['points'] = 10

for alien in aliens[0:5]:
    print(alien) #Shows all five items are modified even though intended for first 3

Вот где все прошло хорошо -

#Case-2
aliens = []

for alienNumber in range(30):
    newAlien = {'color': 'green', 'speed': 'slow', 'points': 5}
    aliens.append(newAlien)

print(aliens) #prints whole list, 30 dicts are added

for alien in aliens[0:3]:
    if alien['color'] == 'green':
        alien['color'] = 'yellow'
        alien['speed'] = 'medium'
        alien['points'] = 10

for alien in aliens[0:5]:
    print(alien)  #Here only first 3 items are modified, as intended

Помогите мне понять поведение for l oop здесь. Предполагается, что l oop только добавляет предметы и ничего больше. Как объявление нового словаря за пределами для l oop меняет способ изменения элементов , позже ?

Ответы [ 3 ]

2 голосов
/ 28 марта 2020

Не думайте с точки зрения переменных. Думайте с точки зрения объектов.

В случае, когда вы определяете (не объявляете - python на самом деле не имеет объявлений переменных) переменную вне l oop, вы создаете объект один раз и переменная продолжает ссылаться на тот же объект, когда вы изменяете его . Затем вы продолжаете добавлять один и тот же объект в список

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

1 голос
/ 28 марта 2020

В случае 1, вы определили словарь newAlien вне для l oop, поэтому при добавлении в список он будет ссылаться на тот же объект, а при изменении значения этого объекта с green на yellow в секунду для l oop, тогда это изменит значение этого объекта. Как я уже говорил, тот же объект относится ко всем. Таким образом, он изменит все значения с green на yellow для всего списка.

В случае 2 вы определили словарь newAlien в l oop так, в каждой итерации newAlien будет ссылаться на новый объект, поэтому, когда вы изменяете это значение с green на yellow, оно будет изменять это значение только для конкретного элемента, потому что оно ссылается на другой объект .

Попробуйте напечатать идентификатор newAlien, как показано ниже, и убедитесь, что в случае-1, как это указано на том же идентификаторе, а в случае-2, укажите другой идентификатор

case -1

newAlien = {'color': 'green', 'speed': 'slow', 'points': 5}

for alienNumber in range(30):
   print(id(newAlien))
   aliens.append(newAlien)

case-2

for alienNumber in range(30):
    newAlien = {'color': 'green', 'speed': 'slow', 'points': 5}
    print(id(newAlien))
    aliens.append(newAlien)

Пожалуйста, запустите оба кода и посмотрите вывод оператора print

0 голосов
/ 28 марта 2020

Чтобы лучше понять, попробуйте выполнить следующий фрагмент кода:

#Case-3
aliens = []
newAlien = {'color': 'green', 'speed': 'slow', 'points': 5}

for alienNumber in range(30):
   aliens.append(newAlien)

print(aliens) #Prints the whole list, showing adding dicts went just fine

aliens[6]['color'] = 'red'
aliens[6]['points'] = 8
aliens[6]['speed'] = 'fast'

for alien in aliens[0:5]:
    print(alien) #Shows all five items are modified even though intended only for the 7th item.

Объяснение

Причина этого не в что вы изменили все 30 предметов в списке. На самом деле, в этом случае вы добавили 30 ссылок на объекты словаря newAlien в список aliens. Таким образом, даже если вы измените один элемент dict, как я пытался сделать в этом случае, когда вы пытаетесь напечатать все 30 элементов, все они будут одинаковыми и, следовательно, будет казаться, что все они были изменены. Когда в действительности есть один newAlien объект словаря, и вы изменили его. Теперь, когда вы попытаетесь получить к нему доступ с использованием любой из 30 ссылок на объекты, вы напечатаете только измененный объект.

Даже в Case-1 условие if alien['color'] == 'green': верно только для первого элемента в aliens список. Затем объект newAlien изменяется, и после этого атрибут color для всех 30 элементов становится желтым . Вы можете проверить это, напечатав сообщение внутри l oop в условии if, чтобы увидеть, сколько раз условие оценивается как true.

В Case-2 вы создаете новый объект newAlien каждый время, когда l oop повторяется и сохраняет ссылку на них. Таким образом, существует 30 различных newAlien словарей, и вы меняете один из них независимо от других.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...