В этой заметке разберем классический подвох, на котором спотыкаются даже опытные разработчики. Рассмотрим код:
a = [[]] * 3
a[0].append(1)
print(a)
Что будет на выводе в консоли? A)
[[1], [], []] B) [[1]] C) [[1], [1], [1]] D) ОшибкаПравильный ответ: C) [[1], [1], [1]]
А если мы увеличим вложенность списков?
a = [[[]]] * 3
a[0].append(1)
print(a)
Что выведет? Подумайте, прежде чем открывать ответ.
Правильный ответ: [[[], 1], [[], 1], [[], 1]]
📚 Ошиблись? Тогда давайте разбираться. Теория того как работает умножение списков
▪️ 1. Главное правило: Операция
[x] * n работает так: ➖ Создается объект
x➖ Создается список из
n элементов➖ Каждый элемент — это ссылка на один и тот же объект
xЭто называется поверхностным (shallow) копированием.
a = [[]] * 3 Реально в памяти: a = [ссылка_на_список, ссылка_на_список, ссылка_на_список] ▪️ 2. Почему с числами всё проще, а со списками — нет?
b = [1] * 3
b[0] = 5
print(b) # [5, 1, 1] всё работает
Числа — неизменяемые. Когда мы пишем
b[0] = 5, мы не меняем объект 1, а переназначаем ссылку на новый объект 5. Остальные элементы продолжают ссылаться на 1.Со списками иначе:
a = [[]] * 3
a[0].append(1) # МЕНЯЕМ сам объект, а не переназначаем ссылку
Метод
.append() изменяет существующий список, не создавая новый. Поэтому изменения видны через все ссылки.a = [[]] * 3
a[0].append(1)
print(a) # [[1], [1], [1]]
a = [[[]]] * 3
a[0].append(1)
print(a) # [[[], 1], [[], 1], [[], 1]]
a = [[[]]] * 3
a[0][0].append(1) # Два индекса!
print(a) # [[[1]], [[1]], [[1]]]
a = [[[]]] * 3
a[0] = 100 # ПЕРЕНАЗНАЧАЕМ ссылку
print(a) # [100, [[ ]], [[ ]]]
a = [[], [], []] # уже три разных списка
a[0].append(1) # меняем только первый
print(a) # [[1], [], []]
▪️Как создать независимые списки?
a = [[] for _ in range(3)]
a[0].append(1)
print(a) # [[1], [], []]
a = [[[]] for _ in range(3)]
a[0].append(1)
print(a) # [[[], 1], [[]], [[]]]
Глубокое копирование:
import copy
a = [copy.deepcopy([[]]) for _ in range(3)]
Классика циклом:
a = []
for _ in range(3):
a.append([])
1. Умножение списков ≠ создание копий объектов — это создание копий ссылок.
2. Для изменяемых объектов (списки, словари, множества) проблема особенно заметна.
3. Для неизменяемых (числа, строки, кортежи) проблема скрыта, но механизм тот же.
4. Всегда используй генератор списков, если нужны независимые вложенные структуры.
5.
a[0].append() — изменение объекта; a[0] = ... — переназначение ссылки▪️Для проверки понимания задачи. Что выведет этот код?
a = [[0]] * 3
a[1][0] = 5
print(a)
#программирование #python #задачи #алгоритмы #computer_science #собеседования
💡 Physics.Math.Code // @physics_lib
