Модуль 3 · Списки и циклы

Справочник по Python · модуль 3 · списки и циклы

Глава 3.10

3.10

Методы списков:
добавление и удаление

append(), extend(), insert() и remove() изменяют исходный список: добавляют один элемент, расширяют набор, вставляют по позиции или удаляют по значению.

Категория
Методы list
Уровень
Начальный
Связанные темы
Списки, индексы, for
Зачем это знать
Управлять содержимым списка

§1Что такое метод

Метод — действие, принадлежащее объекту. Сначала пишется список, затем точка и вызов метода: список.метод(аргументы).

Схемаобъект, точка, метод
numbers.append(4)
│       │      └ аргумент
│       └ метод списка
└ список

Отличие метода от функции — в форме записи. len(numbers) — функция, список передан ей аргументом. numbers.append(4) — метод, он вызван у самого списка и работает именно с ним.

Методы принадлежат типу

У списка есть append(), а у строки его нет: набор методов определяется типом объекта. Поэтому "текст".append("!") вызовет AttributeError.

§2append(): один элемент

append(value) добавляет один объект в конец списка.

main.pyдобавление числа
numbers = [1, 2, 3]
numbers.append(4)
print(numbers)
Выводновый последний элемент
[1, 2, 3, 4]

Слово «один» здесь ключевое. Аргумент может быть списком, строкой, словарём или другим объектом — но добавляется он целиком, как единственный новый элемент.

main.pyвложенный список — один элемент
data = [1, 2]
data.append([10, 20])
print(data)
print(len(data))
Выводдлина стала равна 3, а не 4
[1, 2, [10, 20]]
3

Список [10, 20] занял ровно одну позицию — с индексом 2. Чтобы вместо этого добавить два отдельных числа, нужен extend().

§3append() и результат None

append() меняет существующий список и не создаёт новый. Возвращать ему нечего, поэтому результат вызова равен None.

main.pyметод работает на месте
numbers = [1, 2]
result = numbers.append(3)

print(numbers)
print(result)
Выводизменение и возвращаемое значение
[1, 2, 3]
None
Не присваивайте результат обратно

Запись numbers = numbers.append(3) сначала изменит список, а затем запишет в numbers значение None — и список будет потерян. Следующая же строка с numbers.append(...) выдаст AttributeError. Правило простое: методы, меняющие список на месте, вызывают отдельной строкой.

§4Накопление списка в цикле

Самый частый сценарий с append() — постепенно собрать результат. Список создают пустым, а цикл наполняет его по одному элементу.

main.pyсписок квадратов
squares = []

for number in range(1, 6):
    squares.append(number * number)

print(squares)
Выводпять накопленных значений
[1, 4, 9, 16, 25]
numberДобавляетсяsquares после шага
11[1]
24[1, 4]
39[1, 4, 9]
416[1, 4, 9, 16]
525[1, 4, 9, 16, 25]

Строка squares = [] обязательно стоит до цикла. Внутри цикла она обнуляла бы список на каждом повторе, и в результате остался бы один последний элемент.

§5extend(): несколько элементов

extend(iterable) перебирает переданный объект и добавляет каждый его элемент отдельно.

main.pyрасширение другим списком
numbers = [1, 2, 3]
numbers.extend([4, 5, 6])
print(numbers)
Выводтри новых элемента
[1, 2, 3, 4, 5, 6]

Аргумент должен быть итерируемым — таким, который можно перебрать циклом. Строка этому условию удовлетворяет, и потому разбирается на отдельные символы.

main.pyстрока распадается на символы
letters = ["a"]
letters.extend("bc")
print(letters)
Выводдобавились два символа
['a', 'b', 'c']

Число итерируемым не является: numbers.extend(5) вызовет TypeError. Для одного значения нужен append().

§6append() или extend()

Это самая частая развилка в теме. Разница в одном: append() кладёт аргумент целиком, extend() сначала его разбирает.

Схемаисходный список [1, 2]
append([3, 4]) → [1, 2, [3, 4]]   длина 3
extend([3, 4]) → [1, 2,  3,  4 ]   длина 4
ВызовЧто добавляетсяРезультат
items.append([3, 4])один вложенный список[1, 2, [3, 4]]
items.extend([3, 4])два отдельных элемента[1, 2, 3, 4]
items.append("ab")одна строка[1, 2, "ab"]
items.extend("ab")два символа[1, 2, "a", "b"]

Практическое правило: добавляете одно значение — append(); присоединяете содержимое другой коллекции — extend().

§7insert(): вставка по индексу

insert(index, value) вставляет один элемент перед указанной позицией. Всё, что стояло правее, сдвигается на одну позицию.

main.pyвставка между 1 и 2
numbers = [1, 2, 3]
numbers.insert(1, 100)
print(numbers)
Выводостальные элементы сдвинулись
[1, 100, 2, 3]
Схемачто происходит с позициями
было:  индекс 0 → 1,  индекс 1 → 2,  индекс 2 → 3
стало: индекс 0 → 1,  индекс 1 → 100, индекс 2 → 2, индекс 3 → 3
Вызов для [1, 2, 3]РезультатКомментарий
insert(0, 9)[9, 1, 2, 3]вставка в начало
insert(1, 9)[1, 9, 2, 3]перед элементом с индексом 1
insert(100, 9)[1, 2, 3, 9]слишком большой индекс — вставка в конец, без ошибки
insert(-1, 9)[1, 2, 9, 3]перед последним, а не в конец

Две последние строки часто удивляют. insert() в отличие от индексного доступа не выдаёт IndexError: выходящий за границы индекс он просто прижимает к краю. А insert(-1, value) вставляет перед последним элементом — чтобы добавить в конец, нужен append().

§8remove(): удаление значения

remove(value) ищет значение слева направо и удаляет только первое совпадение.

main.pyпервое вхождение числа 2
numbers = [1, 2, 3, 2]
numbers.remove(2)
print(numbers)
Выводвторая двойка осталась
[1, 3, 2]

Важно различать два способа удаления: remove() работает по значению, а pop() и del — по индексу. Они разбираются в главе 3.11.

Если значения в списке нет, возникает ValueError и программа останавливается. Когда отсутствие допустимо, перед удалением ставят проверку in.

main.pyбезопасное удаление
numbers = [1, 2, 3]
value = 10

if value in numbers:
    numbers.remove(value)

print(numbers)
Выводошибки нет, список не изменился
[1, 2, 3]

§9Изменение на месте

Все четыре метода объединяет одно свойство: они меняют существующий список и возвращают None. Новый объект при этом не создаётся.

МетодЧто делаетВозвращает
append(value)добавляет один элемент в конецNone
extend(values)добавляет элементы коллекцииNone
insert(index, value)вставляет перед позициейNone
remove(value)удаляет первое совпадениеNone

Пока список известен под одним именем, эта деталь незаметна. Она становится важной, как только имён становится два.

§10Два имени — один список

Присваивание backup = tasks не копирует список. Оно даёт тому же самому списку второе имя.

Схемадве ссылки на один объект
tasks  ─┐
        ├─→ ["хлеб"]
backup ─┘
main.pyизменение через одно имя
tasks = ["хлеб"]
backup = tasks

tasks.append("врач")

print(tasks)
print(backup)
Выводизменились оба имени
['хлеб', 'врач']
['хлеб', 'врач']

Никакой копии не было. append() изменил единственный существующий список, и оба имени показывают на него же — поэтому «резервная копия» изменилась вместе с оригиналом.

tasks→['хлеб', 'врач']
backup→тот же объект
Почему это важно именно здесь

Пока переменные хранили числа и строки, разницы между «скопировать значение» и «дать второе имя» не было: неизменяемое значение изменить нельзя в принципе. У списка она появляется — и объясняет большую часть загадочных ошибок с коллекциями.

§11Как получить независимый список

Если нужен именно отдельный список, его создают явно — методом copy().

main.pyкопия не меняется вместе с оригиналом
tasks = ["хлеб"]
backup = tasks.copy()

tasks.append("врач")

print(tasks)
print(backup)
Выводтеперь это два разных списка
['хлеб', 'врач']
['хлеб']
ЗаписьЧто получится
backup = tasksвторое имя того же списка
backup = tasks.copy()новый список с теми же элементами
backup = list(tasks)то же самое, другая запись
backup = tasks[:]то же самое через срез

У копирования есть тонкость с вложенными списками — она разбирается в главе 3.12 вместе с deepcopy().

§12Метод или новый список

Тот же результат иногда можно получить операцией +. Разница в том, что она не трогает исходный список, а строит новый.

main.pyсложение списков
a = [1, 2]
b = a + [3]

print(a)
print(b)
Выводисходный список не изменился
[1, 2]
[1, 2, 3]
ЗаписьИсходный списокРезультат
a.extend([3])изменяетсяNone
b = a + [3]не изменяетсяновый список

В цикле предпочтительнее append(): запись result = result + [value] создаёт новый список на каждом повторе и потому работает заметно медленнее.

§13Все четыре метода вместе

Небольшой сквозной пример — список покупок, который меняется по ходу программы.

main.pyкорзина
cart = ["хлеб", "молоко"]

cart.append("яйца")
cart.insert(0, "вода")
cart.extend(["сыр", "чай"])
cart.remove("молоко")

print(cart)
print(len(cart))
Выводитоговая корзина
['вода', 'хлеб', 'яйца', 'сыр', 'чай']
5
Строкаcart после неё
исходный список['хлеб', 'молоко']
append("яйца")['хлеб', 'молоко', 'яйца']
insert(0, "вода")['вода', 'хлеб', 'молоко', 'яйца']
extend(["сыр", "чай"])['вода', 'хлеб', 'молоко', 'яйца', 'сыр', 'чай']
remove("молоко")['вода', 'хлеб', 'яйца', 'сыр', 'чай']

Всё это время cart — один и тот же список: он не пересоздавался ни разу, менялось только его содержимое.

§14Частые ошибки

Что написаноЧто произойдётКак правильно
numbers = numbers.append(4)в numbers попадёт None, список потеряетсяnumbers.append(4)
numbers.append(4, 5)TypeError: передано два значенияnumbers.extend([4, 5])
letters.extend("abc") для одной строкистрока разделится на символыletters.append("abc")
numbers.extend(5)TypeError: число нельзя перебратьnumbers.append(5)
squares = [] внутри циклана каждом повторе список обнуляетсясоздавать пустой список до цикла
numbers.remove(10), когда десятки нетValueErrorсначала проверить 10 in numbers
ожидать, что remove(2) удалит все двойкиудалится только перваяповторить удаление или построить новый список
backup = tasks как резервная копияоба имени меняются вместеbackup = tasks.copy()
items.insert(-1, value) для добавления в конецэлемент встанет перед последнимitems.append(value)

§15Проверьте себя

Чем append() отличается от extend()?

append() добавляет один объект целиком, а extend() перебирает переданную коллекцию и добавляет каждый её элемент отдельно.

Какой будет длина списка [1, 2] после append([3, 4])?

3. Вложенный список занимает одну позицию.

Что возвращает insert()?

None: метод меняет список на месте.

Почему после backup = tasks изменение tasks видно в backup?

Присваивание не копирует список, а даёт ему второе имя. Оба имени ссылаются на один объект.

Что удаляет remove(value)?

Первое вхождение указанного значения. Если значения нет — ValueError.

Куда вставит элемент items.insert(100, 9) для списка из трёх элементов?

В конец. Слишком большой индекс не вызывает ошибку, а прижимается к краю списка.

§16Шпаргалка

В конецitems.append(value)
Расширитьitems.extend(values)
По позицииitems.insert(index, value)
По значениюitems.remove(value)
Безопасно удалитьif value in items:
Независимая копияbackup = items.copy()
Новый списокjoined = a + b

Итог главы

append() добавляет один объект, extend() — элементы итерируемого объекта, insert() вставляет по индексу, а remove() удаляет первое совпавшее значение. Все четыре метода изменяют список на месте и возвращают None, поэтому их результат не присваивают обратно. И поэтому же присваивание backup = items копией не является: для независимого списка нужен copy().

Глава 3.10 · Методы списков. Часть 1 · Справочник по Python Автор: Макаров Максим Николаевич