Справочник по Python · модуль 3 · списки и циклы
Глава 3.12
Методы списков:
порядок, копия, проверка
reverse() меняет порядок, copy() создаёт копию, len() считает элементы, del удаляет, а in проверяет наличие значения.
- Категория
- Методы и операции коллекций
- Уровень
- Начальный
- Связанные темы
- Списки, вложенные структуры, индексы
- Зачем это знать
- Разворачивать, копировать и проверять коллекции
§1reverse(): изменить порядок
reverse() разворачивает исходный список на месте и возвращает None.
numbers = [1, 2, 3, 4] result = numbers.reverse() print(numbers) print(result)
[4, 3, 2, 1] None
Метод не сортирует, а только переворачивает: элементы идут в обратном порядке относительно того, в каком лежали. Для сортировки по убыванию точнее писать numbers.sort(reverse=True), а не сортировать и затем разворачивать.
§2Новый обратный список
Если исходный порядок нужно сохранить, используют срез с шагом -1: он строит новый список, не трогая старый.
numbers = [1, 2, 3, 4] backward = numbers[::-1] print(numbers) print(backward)
[1, 2, 3, 4] [4, 3, 2, 1]
| Запись | Исходный список | Результат |
|---|---|---|
items.reverse() | изменяется | None |
items[::-1] | не изменяется | новый список |
reversed(items) | не изменяется | объект для перебора, не список |
reversed() удобен прямо в заголовке цикла: for item in reversed(items). Чтобы получить из него именно список, его оборачивают в list().
§3copy(): поверхностная копия
copy() создаёт новый список с теми же элементами. Как показала глава 3.10, простое присваивание такого эффекта не даёт — оно лишь добавляет второе имя.
original = [1, 2, 3] copied = original.copy() copied[0] = 100 print(original) print(copied)
[1, 2, 3] [100, 2, 3]
b = a a ─┐
b ─┴─→ [1, 2, 3] один объект
b = a.copy() a ───→ [1, 2, 3]
b ───→ [1, 2, 3] два объекта
§4Три способа скопировать
Одинаковый результат дают три разные записи. Выбор между ними — вопрос привычки и читаемости.
numbers = [1, 2, 3, 4] print(numbers.copy()) print(list(numbers)) print(numbers[:])
[1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 4]
| Запись | Особенность |
|---|---|
items.copy() | самая явная, читается как «копия» |
list(items) | работает с любой коллекцией, не только со списком |
items[:] | короткая, но неочевидная для начинающих |
Все три создают поверхностную копию — новый внешний список с теми же самыми элементами внутри. Что это значит на практике, показывают §6 и §7.
§5== и is
Есть два разных вопроса: «одинаковое ли содержимое?» и «один ли это объект?». Для них существуют два разных оператора.
a = [1, 2] b = a.copy() c = a print(a == b) print(a is b) print(a is c)
True False True
| Оператор | Что проверяет | Когда использовать |
|---|---|---|
== | совпадает ли содержимое | почти всегда |
is | это один и тот же объект | проверить, копия перед вами или второе имя |
Строка a is c даёт True — это и есть признак того, что c = a копией не было. Оператор is в обычном коде нужен редко; его основное штатное применение — проверка if value is None.
§6Вложенные списки
Поверхностная копия копирует только внешний список. Элементы в неё переносятся как есть — а если элемент сам является списком, обе коллекции продолжают ссылаться на один и тот же вложенный объект.
original ──→ [ • , • ]
│ │
▼ ▼
[1,2] [3,4] ← общие
▲ ▲
│ │
copied ──→ [ • , • ]
original = [[1, 2], [3, 4]] copied = original.copy() copied[0][0] = 100 print(original) print(copied)
[[100, 2], [3, 4]] [[100, 2], [3, 4]]
При этом внешние списки всё-таки независимы. Добавление элемента в копию оригинала не касается:
copied.append([9, 9]) print(original) print(copied)
[[100, 2], [3, 4]] [[100, 2], [3, 4], [9, 9]]
Копия защищает от изменений первого уровня: copied[0] = ..., copied.append(...), copied.remove(...). Изменения внутри элементов — copied[0][0] = ..., copied[0].append(...) — видны в обеих коллекциях.
§7deepcopy(): все уровни
Когда нужна независимость на всех уровнях вложенности, берут deepcopy() из модуля copy. Он проходит структуру целиком и создаёт новые объекты для каждого уровня.
import copy original = [[1, 2], [3, 4]] copied = copy.deepcopy(original) copied[0][0] = 100 print(original) print(copied)
[[1, 2], [3, 4]] [[100, 2], [3, 4]]
| Способ | Внешний список | Вложенные списки |
|---|---|---|
copied = original | общий | общие |
copied = original.copy() | новый | общие |
copied = copy.deepcopy(original) | новый | новые |
Для плоского списка чисел или строк copy() достаточно: числа и строки неизменяемы, и «поделиться» ими безопасно. deepcopy() нужен там, где внутри лежат списки, словари или другие изменяемые объекты.
§8len(): количество элементов
len(collection) — встроенная функция, а не метод списка. Она возвращает количество элементов и работает со строками, кортежами, словарями и множествами.
print(len([10, 20, 30])) print(len("Python")) print(len([[1, 2], [3, 4]]))
3 6 2
Третий пример показывает важное: для вложенного списка len() считает только верхний уровень — в нём два элемента, и каждый из них список. Та же функция работает с кортежами, словарями и множествами из модуля 4. Записи items.len() не существует — это функция, а не метод.
§9del: удаление
del — оператор, а не метод, поэтому и записывается иначе: ключевое слово перед тем, что удаляют. Он может убрать элемент по индексу, целый срез или само имя переменной.
numbers = [1, 2, 3, 4, 5] del numbers[1] print(numbers) del numbers[1:3] print(numbers)
[1, 3, 4, 5] [1, 5]
Второе удаление работает уже с изменённым списком: срез 1:3 убирает из [1, 3, 4, 5] элементы 3 и 4. Правая граница среза не включается — как и в range().
del numbers без индекса удаляет само имя. Если тот же список доступен под другим именем, он остаётся доступным через него. Отдельные элементы строк и кортежей удалить нельзя: эти типы неизменяемы.
§10in и not in
in проверяет наличие равного элемента, а not in — его отсутствие. Результат всегда логический, поэтому его удобно ставить прямо в условие.
numbers = [10, 20, 30] print(20 in numbers) print(50 not in numbers) print("th" in "Python")
True True True
Третья строка показывает важное различие: для строки оператор in ищет непрерывный фрагмент, а не отдельный символ. Для списка правило другое — о нём следующий параграф.
§11Подсписок как элемент
Для списка in не ищет непрерывный фрагмент. Он сравнивает проверяемое значение с каждым отдельным элементом целиком.
print([2, 3] in [1, 2, 3, 4]) print([2, 3] in [1, [2, 3], 4])
False True
В первом случае элементы списка — числа 1, 2, 3, 4, и ни один из них не равен списку [2, 3]. Во втором случае такой элемент действительно есть.
| Проверка | Что ищет in |
|---|---|
"th" in "Python" | непрерывную подстроку |
[2, 3] in [1, 2, 3] | элемент, равный списку [2, 3] |
§12in как защита от ошибок
Проверка in — стандартный способ не дать программе упасть там, где методы выбрасывают исключение.
numbers = [10, 20, 30] value = 99 if value in numbers: numbers.remove(value) else: print("нечего удалять")
нечего удалять
| Опасная запись | Что случится | Проверка перед ней |
|---|---|---|
items.remove(value) | ValueError | if value in items: |
items.index(value) | ValueError | if value in items: |
items.pop() | IndexError | if len(items) > 0: |
items[index] | IndexError | if index < len(items): |
§13Метод, функция или оператор
За три главы набралось три разные формы записи. Различать их полезно: от формы зависит и синтаксис, и то, меняется ли исходная коллекция.
| Форма | Как выглядит | Примеры |
|---|---|---|
| метод | items.действие() | append(), sort(), reverse(), copy() |
| функция | действие(items) | len(), sum(), sorted(), reversed() |
| оператор | ключевое слово или знак | del, in, not in, +, is |
Закономерность почти без исключений: методы списка меняют его на месте и возвращают None, а функции ничего не меняют и возвращают новый результат. Пара sort() / sorted() — самая наглядная иллюстрация этого правила.
§14Частые ошибки
| Что написано | Что произойдёт | Как правильно |
|---|---|---|
items = items.reverse() | в переменную попадёт None | items.reverse() |
copied = original как копия | оба имени указывают на один список | copied = original.copy() |
ожидать полной независимости от nested.copy() | изменение внутреннего списка видно в обеих коллекциях | copy.deepcopy(), когда нужны независимые уровни |
deepcopy(items) без импорта | NameError | import copy и copy.deepcopy(items) |
items.len() | AttributeError | len(items) |
items.del(0) | SyntaxError: del — оператор | del items[0] |
del text[0] | TypeError: строка неизменяема | создать новую строку |
[2, 3] in [1, 2, 3, 4] | получится False | помнить, что список ищет целый элемент |
a is b для сравнения содержимого | у равных списков получится False | a == b |
§15Проверьте себя
Меняет ли reverse() исходный список?
Да. Метод работает на месте и возвращает None. Новый список даёт срез items[::-1].
В чём разница между == и is?
== сравнивает содержимое, is проверяет, что это один и тот же объект. У копии содержимое равно, а объект другой.
Почему вложенный список меняется и в поверхностной копии?
Новый создаётся только внешний список; вложенные изменяемые объекты остаются общими.
Когда copy() достаточно, а когда нужен deepcopy()?
copy() хватает для плоских списков из чисел и строк. deepcopy() нужен, если внутри лежат списки, словари или другие изменяемые объекты.
Чему равна len([[1, 2], [3, 4]])?
2: считается только верхний уровень.
Является ли len() методом списка?
Нет, это встроенная функция: len(items).
§16Шпаргалка
items.reverse()items[::-1]copied = items.copy()copy.deepcopy(items)a is blen(items)del items[index]value in itemsИтог главы
reverse() меняет порядок исходного списка, copy() создаёт поверхностную копию, len() считает элементы верхнего уровня, del удаляет по позиции или срезу, а in проверяет наличие отдельного элемента. Поверхностная копия защищает только внешний уровень: для вложенных структур нужен deepcopy().