Изменяемые объекты и память
Источник определяет изменяемые типы как структуры, которые можно менять после создания без создания нового объекта. Для списков это означает, что append(), remove(), sort() и присваивание по индексу работают на месте.
Python хранит объекты в динамической памяти, а переменные связывает с объектами по ссылке. Поэтому имя переменной не является коробкой со значением, а скорее ярлыком, указывающим на объект.
numbers = [1, 2, 3]
alias = numbers
alias.append(4)
print(numbers)
print(alias)
print(id(numbers) == id(alias))Неизменяемые объекты
Строки, числа и кортежи нельзя изменить на месте. Операция, похожая на изменение, создаёт новый объект и привязывает имя к нему.
Это объясняет, почему text2 += ' world' не меняет text1, хотя изначально обе переменные могли ссылаться на одну строку.
text1 = "hello"
text2 = text1
text2 += " world"
print(text1)
print(text2)
print(id(text1) == id(text2))Вложенные коллекции
Вложенная коллекция содержит другие коллекции. Таблица часто моделируется списком списков: внешний список хранит строки, внутренние списки хранят значения строки.
Каждый дополнительный уровень вложенности требует ещё одного индекса. Если выражение становится трудно читать, промежуточный список лучше сохранить в переменную.
list_elements = [[1, 2, 3], [4, 5], 6, [7, [8, [9], 10]]]
first_row = list_elements[0]
print(first_row[1])
print(list_elements[3][1][2])
print(list_elements[3][1][1][0])Изменение вложенных списков
Если элемент внутри списка сам является списком, его можно изменить через ссылку. Это удобно для таблиц, но опасно при копировании.
При изменении во время обхода используйте enumerate(), чтобы иметь и индекс строки, и индекс элемента.
matrix = [[1, 2], [3, 4], [5, 6]]
for row_index, row in enumerate(matrix):
for column_index, value in enumerate(row):
matrix[row_index][column_index] = value + 1
print(matrix)Оператор del
del удаляет привязку имени, элемент по индексу или срез. Он не возвращает значение, поэтому для получения удалённого элемента нужен pop().
Удаление имени не обязательно удаляет сам объект: если на объект есть другая ссылка, объект остаётся доступным.
numbers = [10, 20, 30, 40, 50]
del numbers[1:3]
print(numbers)
old_numbers = [1, 2, 3]
new_numbers = old_numbers
del old_numbers
print(new_numbers)Поверхностная и глубокая копия
Поверхностная копия создаёт новый внешний список, но вложенные изменяемые объекты остаются теми же самыми объектами.
Глубокая копия через copy.deepcopy() рекурсивно копирует вложенные структуры. Это медленнее, зато изменения в копии не затрагивают оригинал.
import copy
original = [[1, 2], [3, 4]]
shallow = original.copy()
deep = copy.deepcopy(original)
shallow[0][0] = 99
deep[1][0] = 0
print(original)
print(shallow)
print(deep)