Python есть ли в примере разница в формировании вложенного списка через генераторы списков и в чем она заключается?

Рейтинг: 0Ответов: 2Опубликовано: 09.03.2023

Объясните пожалуйста, почему когда формирую вложенный список через одну строчку:

new_list = [[0]*n for j in range(n)]

То все работает и соответствует заданию (все 1 по диагонали), когда через две строчки вывожу вложенный список, то конечный результат отличается (все 1 во всех элементах)? Хотя промежуточный результат - вложенный список из 0 одинаковый до циклов и в том и другом случае.

1) Вот таким способом:

new_list = [[0]*n for j in range(n)]
print(new_list, type(new_list))

for i in range(n):
    new_list[i][i] = 1

for i in new_list:
    print(*i)

То в списке n * n (4*4) выводит 0, а по диагонали 1 в соответствии с заданием:

[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] <class 'list'>
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1

2) До этого попробовал формировать начальный список из 0 через 2 следующие строки вместо одной:

line = [0 for i in range(n)]
new_list = [line for j in line]

То начальный список из 0 формируется такой же, как если через одну строку делать (проверяю через print() сам список и его тип):

[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] <class 'list'>

Конечный результат полностью состоит из 1 и не соответствует тому чтобы 1 были только по диагонали, хотя начальный список из 0 вроде бы одинаковый?:

n = int(input())
line = [0 for i in range(n)]
new_list = [line for j in line]
print(new_list, type(new_list))

for i in range(n):
    new_list[i][i] = 1

for i in new_list:
    print(*i)

Вывод:

[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] <class 'list'>

1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1

Ответы

▲ 3

Тут дело в мутабельности. Проблема в том что line это ссылка и дублируется одна и та же.

Записывая 1 вы записываете её в весь столбец.

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

Можно исправить [list(line) for ....

▲ 1

Объяснение довольно простое - объекты передаются по ссылке. В переменной line у вас получается ссылка на список, которая и копируется много раз. В результате работы второго кода в вашем списке new_list получается n одинаковых ссылок на один и тот же список line. Поэтому первый индекс при обращении к элементу new_list[i][i] можно сказать не учитывается, вы по факту обращаетесь к list[i], таким образом у вас все элементы и становятся равными 1.

new_list = [[0]*n for j in range(n)]
            ^^^^^ новый список на каждой итерации цикла for

line = [0 for i in range(n)]
new_list = [line for j in line]
            ^^^^ ссылка на один и тот же список на каждой итерации цикла for