Перейти к основному содержимому

Получение частей коллекции

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

Для большинства таких задач в Python есть один синтаксис — срез. words[low:high] даёт элементы от индекса low до индекса high, не включая его. Если не указать low, срез начнётся с начала, а если не указать high, он дойдёт до конца. Отрицательный индекс отсчитывается от конца, а третье число задаёт шаг.

  • s[low:high] — от индекса low до high, не включая его
  • s[:n] — первые n элементов
  • s[n:] — всё от индекса n до конца
  • s[-n:] — последние n элементов
  • s[::2] — каждый второй элемент

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

Для задач, которые срез не решает сам по себе, в этом уроке используются функции из модуля itertools и одна-две небольшие вспомогательные функции.

В большинстве примеров на этой странице используются одни и те же данные: слова one two three four five six.


Срез

Срез — это выбор элементов, стоящих на определённых позициях.

Для ряда соседних позиций используйте срез: words[1:4] даёт элементы с индексами 1, 2 и 3. Помните, что конечный индекс не включается: чтобы получить элементы с индексами от 1 до 3, нужно написать 1:4.

Срез может даже пропускать элементы: третье число — это шаг, поэтому words[::2] берёт каждый второй элемент. Для позиций, которые вы перечисляете сами и в своём порядке, используйте генератор списка по списку индексов.

Срез диапазона

words[1:4] начинается с индекса 1 и останавливается перед индексом 4. В результате 3 элемента: ['two', 'three', 'four']. Это новый список, поэтому его изменение не затронет words.

Prompt
Создайте список из слов one, two, three, four, five, six. С помощью среза возьмите элементы от индекса 1 до индекса 4, не включая его. Выведите результат.
Вывод: полная программа на Python
Slice a range

Срез с шагом

У среза words[::2] не указаны начало и конец, поэтому он охватывает весь список, а шаг 2 берёт каждый второй элемент: индексы 0, 2 и 4. Цикл не нужен — шаг входит в синтаксис среза.

Prompt
Создайте список из слов one, two, three, four, five, six. С помощью среза с шагом 2 возьмите каждый второй элемент. Выведите результат.
Вывод: полная программа на Python
Slice with a step

Срез по списку индексов

Позиции 3, 5 и 0 хранятся в списке чисел. Генератор списка берёт слово на каждой позиции, поэтому результат идёт в порядке индексов.

Prompt
Создайте список из слов one, two, three, four, five, six и список индексов 3, 5, 0. С помощью генератора списка по индексам соберите слова на этих позициях в этом порядке. Выведите результат.
Вывод: полная программа на Python
Slice by a list of indices

Take и Drop

Take означает оставить несколько элементов с одного конца. Drop означает отбросить несколько элементов с одного конца и оставить остальные.

В Python и то и другое — срезы. words[:3] берёт первые 3 элемента, а words[-3:] — последние 3. words[1:] отбрасывает первый элемент, а words[:-5] — последние 5.

Есть и версии с условием. Они не считают элементы: они идут с одного конца и останавливаются на первом элементе, который не выполняет условие. В Python они встроены в модуль itertools: takewhile() оставляет элементы с начала, пока условие выполняется, а dropwhile() пропускает элементы, пока условие выполняется, и оставляет остальные. Обе функции возвращают ленивый итератор, поэтому оберните вызов в list(), чтобы увидеть элементы. Для тех же задач с конца списка встроенной функции нет: запустите функцию на reversed(words) и разверните результат обратно с помощью [::-1].

Взять первые или последние n элементов

words[:3] оставляет первые 3 слова. words[-3:] оставляет последние 3, потому что отрицательный индекс отсчитывается от конца.

Prompt
Создайте список из слов one, two, three, four, five, six. С помощью срезов получите первые 3 элемента и последние 3 элемента. Выведите оба результата на отдельных строках.
Вывод: полная программа на Python
Take the first or last n elements

Отбросить первые или последние n элементов

words[1:] пропускает первое слово. words[:-5] пропускает последние 5 слов, поэтому остаётся только one.

Prompt
Создайте список из слов one, two, three, four, five, six. С помощью срезов получите все элементы, кроме первого, и все элементы, кроме последних 5. Выведите оба результата на отдельных строках.
Вывод: полная программа на Python
Drop the first or last n elements

Брать, пока выполняется условие

takewhile() оставляет слова с начала, пока не встретит слово, начинающееся с f. Условие — это lambda, небольшая функция без имени, а startswith() проверяет начало строки. Для конца списка takewhile() проходит по reversed(words) — оставляет слова, пока не встретит three, — а [::-1] разворачивает результат обратно.

Prompt
Возьмите слова one, two, three, four, five, six. С помощью itertools.takewhile оставьте слова с начала, пока слово не начинается с f. Затем оставьте слова с конца, пока слово не равно three: запустите takewhile на развёрнутом списке и разверните результат обратно. Выведите оба результата.
Вывод: полная программа на Python
Take while a condition holds

Отбрасывать, пока выполняется условие

dropwhile() пропускает слова из 3 букв в начале и оставляет всё, начиная с three. Тот же приём с reversed() пропускает слова с конца, пока они содержат букву i. Оператор in проверяет, содержит ли строка фрагмент текста.

Prompt
Возьмите слова one, two, three, four, five, six. С помощью itertools.dropwhile пропустите слова с начала, пока в слове 3 буквы, и оставьте остальные. Затем пропустите слова с конца, пока слово содержит букву i: запустите dropwhile на развёрнутом списке и разверните результат обратно. Выведите оба результата.
Вывод: полная программа на Python
Drop while a condition holds

Разбиение на части

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

В этой версии Python нет встроенной функции для разбиения на части (в более новых версиях добавлена itertools.batched()), но вспомогательная функция занимает одну строку: range(0, len(items), size) даёт начальные индексы 0, 3, 6, …, а срез вырезает от каждого из них один кусок. Срез никогда не читает за пределами списка, поэтому короткий последний кусок получается правильным сам собой.

Результат — список списков, и у каждого куска свои скобки: [[0, 1, 2], [3, 4, 5], …].

Чтобы сразу обработать каждый кусок, поместите куски в генератор списка.

Разбить на части по 3

Числа от 0 до 13 разрезаются на куски по 3. В последнем куске только 2 элемента: [12, 13].

Prompt
Напишите вспомогательную функцию chunked, которая разрезает список на куски заданного размера и сохраняет более короткий последний кусок. Примените её к числам от 0 до 13 с размером 3 и выведите список кусков.
Вывод: полная программа на Python
Split into chunks of 3

Сумма каждой части

Те же куски, но каждый из них складывается встроенной функцией sum(). Сохраняются только суммы: [3, 12, 21, 30, 25].

Prompt
Напишите вспомогательную функцию chunked, которая разрезает список на куски заданного размера. Примените её к числам от 0 до 13 с размером 3, затем сложите каждый кусок с помощью sum() в генераторе списка и выведите список сумм.
Вывод: полная программа на Python
Sum each chunk

Скользящее окно

Окно — это группа соседних элементов. Скользящее окно сдвигает эту группу вдоль списка и собирает все её положения. В отличие от кусков, окна перекрываются.

В Python нет встроенной функции windowed. Это делает генератор списка с items[i:i + size], а небольшая вспомогательная функция windowed позволяет использовать его повторно. С параметром step окно сдвигается сразу на несколько позиций. Если partial равен True, более короткое окно в конце сохраняется, а не отбрасывается.

Окно из 2 элементов, которое сдвигается на 1 шаг, — это то же самое, что составить пару из каждого элемента и следующего за ним. В Python для этого есть классическая идиома zip(words, words[1:]), а в модуле itertools есть готовая функция pairwise().

Окна по 3

В окне 3 слова, и каждый раз оно сдвигается на 1 позицию. range(len(items) - size + 1) останавливается, когда полное окно больше не помещается, поэтому окон 3. Результат — список списков, выведенный в одну строку.

Prompt
Напишите вспомогательную функцию windowed, которая принимает список и размер окна и возвращает каждую группу из такого числа соседних элементов, сдвигаясь каждый раз на одну позицию. Примените её с размером 3 к словам one, two, three, four, five и выведите результат.
Вывод: полная программа на Python
Windows of 3

Окна с шагом и неполные окна

В окне 3 числа, и каждый раз оно сдвигается на 2 позиции. Поскольку partial равен True, последнее окно [9, 10] сохраняется, хотя оно короче.

Prompt
Напишите вспомогательную функцию windowed, которая принимает список, размер окна, шаг и флаг partial. Между окнами она сдвигается на step позиций, а когда partial равен True, она также сохраняет более короткое окно в конце. Примените её к числам от 1 до 10 с размером 3, шагом 2 и partial, равным True, затем выведите результат.
Вывод: полная программа на Python
Windows with a step and partial windows

Сумма каждого окна

Каждое окно из 3 соседних элементов складывается с помощью sum(). Сохраняются только суммы: [6, 9, 12].

Prompt
Создайте список из чисел от 1 до 5. Сложите каждое окно из 3 соседних элементов с помощью sum() в генераторе списка и выведите список сумм.
Вывод: полная программа на Python
Sum each window

Пара из каждого элемента и следующего

zip(letters, letters[1:]) составляет пару из каждого слова и его соседа. Каждая пара — это кортеж, который выводится в круглых скобках: ('one', 'two'). itertools.pairwise() даёт те же пары в готовом виде; здесь она передаёт их в генератор списка, который записывает, длиннее ли первое слово второго.

Prompt
Создайте список из слов one, two, three, four, five. Составьте пары из каждого элемента и следующего за ним, применив zip к списку и к списку без первого элемента, и выведите список пар. Затем с помощью itertools.pairwise пройдите по тем же соседним элементам и запишите, длиннее ли первое слово второго. Выведите список логических значений.
Вывод: полная программа на Python
Pair each element with the next one