Посилання у контейнерах
У списках і кортежах містяться не об'єкти, як може здатись на перший погяд, а посилання на об'єкти. Давайте переконаємось у цьому.
Створимо немутабельний кортеж, який буде містити у собі мутабельний список:
>>> t = ([1, 1], )
>>> l = t[0]
>>> l is t[0]
True
>>>
І тепер змінюємо список, який міститься у кортежі:
>>> l[0] = 7
>>> t
([7, 1],)
>>>
Так виходить що ми змінили незмінюваний кортеж? Ні, кортеж залишився таким як і був. Значенням кортежа є послідовність посилань на об'єкти, і ці посилання ніяк не змінились. Змінився об'єкт, на який вказує одне з посилань, яке міститься в кортежі.
Ще приклад. Створимо список, який містить у собі ще один список з трьох об'єктів:
>>> l = [[0, 0, 0]]
>>> inner_list = l[0]
>>> inner_list
[0, 0, 0]
>>> inner_list is l[0]
True
>>>
А тепер спробуємо зі списка l
зробити матрицю три на три.
Для цього скористаємось мультиплікацією послідовностей:
>>> matrix = l * 3
>>> matrix
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>>
Начебто отримали те, що хотіли. Спробуємо змінити елемент матриці у першому стовпчику першого ряда:
>>> matrix[0][0] = 7
>>> matrix
[[7, 0, 0], [7, 0, 0], [7, 0, 0]]
>>>
Не зовсім те, на що очікували... Що не так?
Уся справа у тому, що операція мультиплікації "розмножує" не самі об'єкти у послідовності, а посилання на них.
Стовримо список l
трошки інакше:
>>> inner_list = [0, 0, 0]
>>> l = [inner_list]
>>> l
[[0, 0, 0]]
>>> l[0] is inner_list
True
>>>
І коли ми використовуємо мультиплікацію, то вираз:
matrix = l * 3
фактично означає:
matrix = [inner_list] * 3
В результаті отримуємо список у якому тричі повторюється посилання inner_list
на один і той самий об'єкт:
[inner_list, inner_list, inner_list]
Отже вираз:
matrix[0][0] = 7
фактично означає:
inner_list[0] = 7
Так що створити матрицю використовуючи мультиплікацію послідовностей як наведено у прикладі нижче нажаль не вийде:
n = 5
matrix = [[0] * n] * n # так повноцінної матриці не отримати!