Подтвердить что ты не робот

Путаница списка 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 копирует первый адрес списка вместо выделения нового блока памяти?

4b9b3361

Ответ 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)]