Справочник по Python · модуль 3 · списки и циклы
Глава 3.10
Методы списков:
добавление и удаление
append(), extend(), insert() и remove() изменяют исходный список: добавляют один элемент, расширяют набор, вставляют по позиции или удаляют по значению.
- Категория
- Методы list
- Уровень
- Начальный
- Связанные темы
- Списки, индексы, for
- Зачем это знать
- Управлять содержимым списка
§1Что такое метод
Метод — действие, принадлежащее объекту. Сначала пишется список, затем точка и вызов метода: список.метод(аргументы).
numbers.append(4) │ │ └ аргумент │ └ метод списка └ список
Отличие метода от функции — в форме записи. len(numbers) — функция, список передан ей аргументом. numbers.append(4) — метод, он вызван у самого списка и работает именно с ним.
У списка есть append(), а у строки его нет: набор методов определяется типом объекта. Поэтому "текст".append("!") вызовет AttributeError.
§2append(): один элемент
append(value) добавляет один объект в конец списка.
numbers = [1, 2, 3] numbers.append(4) print(numbers)
[1, 2, 3, 4]
Слово «один» здесь ключевое. Аргумент может быть списком, строкой, словарём или другим объектом — но добавляется он целиком, как единственный новый элемент.
data = [1, 2] data.append([10, 20]) print(data) print(len(data))
[1, 2, [10, 20]] 3
Список [10, 20] занял ровно одну позицию — с индексом 2. Чтобы вместо этого добавить два отдельных числа, нужен extend().
§3append() и результат None
append() меняет существующий список и не создаёт новый. Возвращать ему нечего, поэтому результат вызова равен None.
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() — постепенно собрать результат. Список создают пустым, а цикл наполняет его по одному элементу.
squares = [] for number in range(1, 6): squares.append(number * number) print(squares)
[1, 4, 9, 16, 25]
| number | Добавляется | squares после шага |
|---|---|---|
1 | 1 | [1] |
2 | 4 | [1, 4] |
3 | 9 | [1, 4, 9] |
4 | 16 | [1, 4, 9, 16] |
5 | 25 | [1, 4, 9, 16, 25] |
Строка squares = [] обязательно стоит до цикла. Внутри цикла она обнуляла бы список на каждом повторе, и в результате остался бы один последний элемент.
§5extend(): несколько элементов
extend(iterable) перебирает переданный объект и добавляет каждый его элемент отдельно.
numbers = [1, 2, 3] numbers.extend([4, 5, 6]) print(numbers)
[1, 2, 3, 4, 5, 6]
Аргумент должен быть итерируемым — таким, который можно перебрать циклом. Строка этому условию удовлетворяет, и потому разбирается на отдельные символы.
letters = ["a"] letters.extend("bc") print(letters)
['a', 'b', 'c']
Число итерируемым не является: numbers.extend(5) вызовет TypeError. Для одного значения нужен append().
§6append() или extend()
Это самая частая развилка в теме. Разница в одном: append() кладёт аргумент целиком, extend() сначала его разбирает.
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) вставляет один элемент перед указанной позицией. Всё, что стояло правее, сдвигается на одну позицию.
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) ищет значение слева направо и удаляет только первое совпадение.
numbers = [1, 2, 3, 2] numbers.remove(2) print(numbers)
[1, 3, 2]
Важно различать два способа удаления: remove() работает по значению, а pop() и del — по индексу. Они разбираются в главе 3.11.
Если значения в списке нет, возникает ValueError и программа останавливается. Когда отсутствие допустимо, перед удалением ставят проверку in.
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 ─┘
tasks = ["хлеб"] backup = tasks tasks.append("врач") print(tasks) print(backup)
['хлеб', 'врач'] ['хлеб', 'врач']
Никакой копии не было. append() изменил единственный существующий список, и оба имени показывают на него же — поэтому «резервная копия» изменилась вместе с оригиналом.
Пока переменные хранили числа и строки, разницы между «скопировать значение» и «дать второе имя» не было: неизменяемое значение изменить нельзя в принципе. У списка она появляется — и объясняет большую часть загадочных ошибок с коллекциями.
§11Как получить независимый список
Если нужен именно отдельный список, его создают явно — методом copy().
tasks = ["хлеб"] backup = tasks.copy() tasks.append("врач") print(tasks) print(backup)
['хлеб', 'врач'] ['хлеб']
| Запись | Что получится |
|---|---|
backup = tasks | второе имя того же списка |
backup = tasks.copy() | новый список с теми же элементами |
backup = list(tasks) | то же самое, другая запись |
backup = tasks[:] | то же самое через срез |
У копирования есть тонкость с вложенными списками — она разбирается в главе 3.12 вместе с deepcopy().
§12Метод или новый список
Тот же результат иногда можно получить операцией +. Разница в том, что она не трогает исходный список, а строит новый.
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Все четыре метода вместе
Небольшой сквозной пример — список покупок, который меняется по ходу программы.
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().