Путаница списка Python
Скажем, у меня есть следующий код:
a_list = [[0]*10]*10
Это создает следующий список:
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
Затем я хочу изменить первый элемент в первом списке:
a_list[0][0] = 23
Я ожидал изменения только первого элемента списка, но на самом деле был изменен первый элемент каждого списка:
[[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[23, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
Мне удалось найти другой способ представить мои данные, чтобы избежать этого, но почему это происходит? Почему изменился не только первый список? Когда я делаю второй *10
, действительно ли Python копирует первый адрес списка вместо выделения нового блока памяти?
Ответы
Ответ 1
Ваша догадка о копировании адресов верна. Подумайте об этом так:
sub_list = [0] * 10
a_list = [sub_list] * 10
Этот код фактически эквивалентен коду, который вы разместили выше. Это означает, что вы фактически меняете один и тот же список sub_list
всякий раз, когда вы меняете любой элемент a_list
. Вы даже можете убедиться в этом, набрав:
a_list = [[0] * 10] * 10
for n in a_list:
print id(n)
И он будет отображаться одинаково для каждого элемента. Чтобы исправить это, вы должны использовать:
a_list = [[0] * 10 for _ in range(10)]
Чтобы создать новый подписок для каждого элемента a_list
.
Ответ 2
Списки содержат ссылки на объекты. Умножение на списки просто повторяет ссылки (к тем же объектам!). Хотя это прекрасно для неизменяемых объектов (например, целых чисел), то, что вы получаете, - это несколько ссылок на один и тот же список.
Создайте отдельные списки с этим шаблоном [[0]*10 for _ in xrange(10)]
.
Ответ 3
Почему изменился не только первый список?
Причина проста: действительно есть только 1 список, а не 10 - как вы уже подозревали:
In [1]: [[0]*10]*10
Out[1]:
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
In [2]: map(id, _)
Out[2]:
[54094624,
54094624,
54094624,
54094624,
54094624,
54094624,
54094624,
54094624,
54094624,
54094624]
Если вы хотите создать 10 списков, вы можете легко достичь этого с помощью выражения типа
[[0]*10 for x in xrange(10)]