Студент Макс узнал, что в Python умножать можно не только числа, но и другие объекты, например, строку на число:
>>> "Max" * 3 'MaxMaxMax'
«Вау!» — подумал Макс — «А что если умножить список на число?»:
>>> [42, 26] * 3 [42, 26, 42, 26, 42, 26]
Значит можно создать двумерный массив очень кратко и элегантно?
>>> [[]] * 3 [[], [], []]
Заполнить его:
arr = [[]] * 3 arr[0].append(10) arr[1].append(20) arr[2].append(30)
Макс ожидал получить:
[[10], [20], [30]]
А вышло:
[[10, 20, 30], [10, 20, 30], [10, 20, 30]]
😯 Как же так?! Дело в том, что умножение списка на число не копирует сам объект, а лишь ссылку на него. Все три элемента arr ссылаются на один и тот же список. Легко проверить, сравнив адреса объектов:
>>> arr[0] is arr[1] True >>> id(arr[0]), id(arr[1]) (4400840776, 4400840776)

Аналогично в случае классов:
class Dummy: ... arr = [Dummy()] * 2 arr[0].x = 10 arr[1].x = 20 print(arr[0].x, arr[0] is arr[1]) # 20 True
А вот с числами, строками и кортежами умножение списка будет работать как ожидал Макс, потому что это неизменяемые типы. Вот такая тонкость, которую нужно знать. Максу следовало бы написать так:
arr = [[] for _ in range(3)] arr[0].append(10) arr[1].append(20) arr[2].append(30) >>> arr [[10], [20], [30]]
Менее кратко, но зато работает без сюрпризов: каждую итерацию создается новый пустой список.
🐉 Специально для канала @pyway. Подписывайтесь на мой канал в Телеграм @pyway 👈