Изменение путем присваивания

Механизм индексирования списков можно использовать для изменения списков. Чтобы изменить один элемент списка, достаточно обратиться к нему по индексу и присвоить ему новое значение:

>>> x = list('abcd')
>>> x
['a', 'b', 'c', 'd']
>>>
>>> x[0] = 'AAA'
>>> x
['AAA', 'b', 'c', 'd']

Указание срезов списка позволяет выполнить множество различных операций по его изменению, но в этом случае любое присваиваемое значение должно быть списком:

>>> x = list('abcd')
>>> x
['a', 'b', 'c', 'd']
>>>
>>> x[1:3] = ['BBB', 'CCC']     # заменяем срез новым списком
>>> x
['a', 'BBB', 'CCC', 'd']

В принципе, заменить срез можно любым итерируемым объектом, который автоматически будет преобразован в список функцией list():

>>> x[1:3] = (0, 0)    # присваиваем кортеж
>>> x
['a', 0, 0, 'd']
>>>
>>> x[1:3] = 'XY'    # присваиваем строку
>>> x
['a', 'X', 'Y', 'd']
>>>
>>> x[1:3] = {'key1': 'spam', 'key2': 'ham'}    # присваиваем ключи словаря
>>> x
['a', 'key1', 'key2', 'd']
>>>
>>> x[1:3] = range(2)     # присваиваем итератор
>>> x
['a', 0, 1, 'd']

Срезы позволяют добавлять списки в начало, в конец, а также в указанную позицию других списков:

>>> x = [3, 4, 5]
>>> x[len(x):] = [6, 7, 8]    # добавление в конец, равносильно x += [6, 7, 8]
>>> x
[3, 4, 5, 6, 7, 8]
>>>
>>> x[:0] = [-1, 0]     # добавление в начало
>>> x
[-1, 0, 3, 4, 5, 6, 7, 8]
>>>
>>> x[2:2] = [1, 2]     # вставка в указанную позицию
>>> x
[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]

Срез можно заменить списком произвольной длины, но это приведет к изменению длины исходного списка:

>>> x = [0, 'A', 'A', 7, 8]
>>>
>>> x[1:3] = [1, 2, 3, 4, 5, 6]    # длина x увеличится
>>> x
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>>
>>> x[2:-2] = ['B', 'B']    # длина x уменьшится
>>> x
[0, 1, 'B', 'B', 7, 8]

Что бы удалить элементы из списка, нужно присвоить пустой список, соответствующему срезу:

>>> x = [1, 'a', 2, 'b', 'c', 3, 4]
>>>
>>> x[1:2] = []    # команда x[1] = [] вернет [1, [], 2, 'b', 'c', 3, 4]
>>> x
[1, 2, 'b', 'c', 3, 4]
>>>
>>> x[2:4] = []
>>> x
[1, 2, 3, 4]

Разреженные срезы, т.е. элементы взятые с заданным шагом так же могут быть изменены, но в этом случае, присваиваемый список должен иметь туже длину что и разреженный срез:

>>> x = [1]*15
>>> x
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
>>>
>>> x[::2] = [0, 0, 0, 0, 0, 0, 0, 0]
>>> x
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]

Если элементы с заданным шагом должны принять какое-то фиксированное значение, то сконструировать подходящий по размеру список можно с помощью функции len:

>>> x[1::2] = [-1]*len(x[1::2])
>>> x
[0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0]

Однако, удалить элементы разреженного среза путем присваивания пустого массива не получится:

>>> x[1::2] = []
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: attempt to assign sequence of size 0 to extended slice of size 7

Для этих целей можно воспользоваться инструкцией del:

>>> del x[1::2]
>>> x
[0, 0, 0, 0, 0, 0, 0, 0]

Инструкция del может использоваться для удаления отдельных элементов списка или его среза:

>>> x = [1, 'a', 2, 'a', 'a', 3, 4]
>>>
>>> del x[1]
>>> x
[1, 2, 'a', 'a', 3, 4]
>>>
>>> del x[2:4]
>>> x
[1, 2, 3, 4]

Но следует помнить, что на самом деле элементы списка - это ссылки на объекты, т.е. del не освобождает память, а лишь удаляет ссылки на объекты. После удаления ссылок, объекты считаются готовыми к утилизации из памяти компьютера, но предсказать момент освобождения памяти очень сложно. Проблему "засорения" памяти, нельзя назвать актуальной, но в тех случаях, когда списки используются в сложных алгоритмах обработки больших файлов (изображения, видео, аудио), то лучше перестраховаться и гарантированно освобождать память с помощью конструкции try ... finally или инструкции with.