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

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

Глава 3.12

3.12

Методы списков:
порядок, копия, проверка

reverse() меняет порядок, copy() создаёт копию, len() считает элементы, del удаляет, а in проверяет наличие значения.

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

§1reverse(): изменить порядок

reverse() разворачивает исходный список на месте и возвращает None.

main.pyобратный порядок
numbers = [1, 2, 3, 4]
result = numbers.reverse()

print(numbers)
print(result)
Выводсписок изменён
[4, 3, 2, 1]
None

Метод не сортирует, а только переворачивает: элементы идут в обратном порядке относительно того, в каком лежали. Для сортировки по убыванию точнее писать numbers.sort(reverse=True), а не сортировать и затем разворачивать.

§2Новый обратный список

Если исходный порядок нужно сохранить, используют срез с шагом -1: он строит новый список, не трогая старый.

main.pyсрез создаёт новый список
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, простое присваивание такого эффекта не даёт — оно лишь добавляет второе имя.

main.pyизменение копии
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Три способа скопировать

Одинаковый результат дают три разные записи. Выбор между ними — вопрос привычки и читаемости.

main.pyодно и то же
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

Есть два разных вопроса: «одинаковое ли содержимое?» и «один ли это объект?». Для них существуют два разных оператора.

main.pyравенство и тождественность
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   ──→ [ • , • ]
main.pyизменение вложенного элемента
original = [[1, 2], [3, 4]]
copied = original.copy()

copied[0][0] = 100

print(original)
print(copied)
Выводизменились оба
[[100, 2], [3, 4]]
[[100, 2], [3, 4]]

При этом внешние списки всё-таки независимы. Добавление элемента в копию оригинала не касается:

main.pyизменение внешнего уровня
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. Он проходит структуру целиком и создаёт новые объекты для каждого уровня.

main.pyглубокая копия
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) — встроенная функция, а не метод списка. Она возвращает количество элементов и работает со строками, кортежами, словарями и множествами.

main.pyдлина разных коллекций
print(len([10, 20, 30]))
print(len("Python"))
print(len([[1, 2], [3, 4]]))
Выводэлементы, символы, вложенные списки
3
6
2

Третий пример показывает важное: для вложенного списка len() считает только верхний уровень — в нём два элемента, и каждый из них список. Та же функция работает с кортежами, словарями и множествами из модуля 4. Записи items.len() не существует — это функция, а не метод.

§9del: удаление

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

main.pyэлемент и срез
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 — его отсутствие. Результат всегда логический, поэтому его удобно ставить прямо в условие.

main.pyпроверка списка и строки
numbers = [10, 20, 30]

print(20 in numbers)
print(50 not in numbers)
print("th" in "Python")
Выводтри истинные проверки
True
True
True

Третья строка показывает важное различие: для строки оператор in ищет непрерывный фрагмент, а не отдельный символ. Для списка правило другое — о нём следующий параграф.

§11Подсписок как элемент

Для списка in не ищет непрерывный фрагмент. Он сравнивает проверяемое значение с каждым отдельным элементом целиком.

main.pyдва разных случая
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 — стандартный способ не дать программе упасть там, где методы выбрасывают исключение.

main.pyпроверка перед удалением и поиском
numbers = [10, 20, 30]
value = 99

if value in numbers:
    numbers.remove(value)
else:
    print("нечего удалять")
Выводвместо ValueError
нечего удалять
Опасная записьЧто случитсяПроверка перед ней
items.remove(value)ValueErrorif value in items:
items.index(value)ValueErrorif value in items:
items.pop()IndexErrorif len(items) > 0:
items[index]IndexErrorif 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()в переменную попадёт Noneitems.reverse()
copied = original как копияоба имени указывают на один списокcopied = original.copy()
ожидать полной независимости от nested.copy()изменение внутреннего списка видно в обеих коллекцияхcopy.deepcopy(), когда нужны независимые уровни
deepcopy(items) без импортаNameErrorimport copy и copy.deepcopy(items)
items.len()AttributeErrorlen(items)
items.del(0)SyntaxError: del — операторdel items[0]
del text[0]TypeError: строка неизменяемасоздать новую строку
[2, 3] in [1, 2, 3, 4]получится Falseпомнить, что список ищет целый элемент
a is b для сравнения содержимогоу равных списков получится Falsea == 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 b
Длинаlen(items)
Удалить по позицииdel items[index]
Наличиеvalue in items

Итог главы

reverse() меняет порядок исходного списка, copy() создаёт поверхностную копию, len() считает элементы верхнего уровня, del удаляет по позиции или срезу, а in проверяет наличие отдельного элемента. Поверхностная копия защищает только внешний уровень: для вложенных структур нужен deepcopy().

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