Почему список списков через * может сломать вам всю логику
На первый взгляд этот код выглядит абсолютно нормально:
# хотим создать таблицу 3x3, заполненную нулями
matrix = [[0] * 3] * 3
# меняем один элемент
matrix[0][1] = 7
print(matrix)
Многие ожидают такой результат:
[[0, 7, 0],
[0, 0, 0],
[0, 0, 0]]
Но на деле получится:
[[0, 7, 0],
[0, 7, 0],
[0, 7, 0]]
И вот это уже выглядит как магия.
Хотя на самом деле это просто особенность ссылок в Python.
Что здесь происходит?
Проблема в этой строке:
matrix = [[0] * 3] * 3
Кажется, будто Python создаёт три независимых списка.
Но нет.
Он создаёт один внутренний список и затем повторяет ссылку на него три раза.
То есть все строки матрицы указывают на один и тот же объект в памяти.
Поэтому изменение одного элемента фактически меняет сразу все «строки».
Как создать матрицу правильно
Нужно, чтобы каждая строка создавалась отдельно:
matrix = [[0] * 3 for _ in range(3)]
matrix[0][1] = 7
print(matrix)
Теперь результат будет корректным:
[[0, 7, 0],
[0, 0, 0],
[0, 0, 0]]
Потому что каждая строка — это уже свой собственный список.
Почему это опасно?
Такая ошибка особенно неприятна, когда вы работаете с:
• двумерными массивами
• динамическим программированием
• таблицами состояний
• игровыми полями
• сетками, картами, матрицами
Код может выглядеть правильно, но логика начнёт ломаться в самых неожиданных местах.
Особенно часто это всплывает в задачах на алгоритмы.
Как быстро запомнить
Если внутри структура изменяемая, то конструкция через * может быть опасной.
То есть вот так:
[ [0] * 3 ] * 3
— подозрительно.
А вот так:
[ [0] * 3 for _ in range(3) ]
— безопасно.
Главное правило
Оператор * хорошо подходит для простых списков,
но плохо подходит для вложенных изменяемых структур.
Именно поэтому матрицы через умножение списков — классическая ловушка Python.