Срезы списков в Python

Настало время перейти к очень интересному инструменту, который Python предоставляет для работы с целыми подмножествами элементов списка: к так называемым срезам (slices).

Синтаксис описания срезов

Срезы встроены в язык и снабжены своим собственным синтаксисом — настолько широко они используются. Срез записывается так же, как записывается обращение к элементу списка по индексу:

some_list[START:STOP:STEP]  

Всего у среза три параметра:

  • START — индекс первого элемента в выборке
  • STOP — индекс элемента списка, перед которым срез должен закончиться. Сам элемент с индексом STOP не будет входить в выборку
  • STEP — шаг прироста выбираемых индексов

Математически говоря, в множество будут входить индексы элементов, которые будут выбраны:

(START, START + STEP, START + 2 * STEP, .., STOP) # STOP не входит в срез  

Например, срез [3:20:5] означает выборку значений с индексами 3, 8, 13 и 18.

При этом любой из трех параметров среза может быть опущен и вместо соответствующего параметра будет выбрано некое значение по умолчанию:

  • Умолчательный START означает «от начала списка»
  • Умолчательный STOP означает «до конца списка включительно»
  • Умолчательный STEP означает «брать каждый элемент»

Вот несколько примеров с разными наборами параметров:

  • [:] или [::] — весь список
  • [::2] — нечетные по порядку элементы
  • [1::2] — четные по порядку элементы
  • [::-1] — все элементы в обратном порядке
  • [5:] — все элементы, начиная с шестого
  • [:5] — все элементы, не доходя до шестого
  • [-2:1:-1] — все элементы от предпоследнего до третьего в обратном порядке. Во всех случаях выборки от большего индекса к меньшему нужно указывать шаг

Срезы могут работать в двух режимах: собственно выборка и присваивание.

Выборка элементов

Срезы-выборки работают со списками, кортежами, строками. Результатом применения выборки всегда становится новое значение соответствующего типа — список, кортеж, строка:

'hello'[2:]  # 'llo'  (1, "foo", True, None)[2:]  # (True, None)  [1, 2, 3, 4, 5][2:]  # [3, 4, 5]  

Сразу сделаем несколько замечаний по использованию выборок:

  • Кортежи чаще всего содержат разнородные элементы, поэтому срезы для них менее полезны, чем распаковка и перепаковка: тяжело удерживать в голове типы элементов вместе с индексами
  • При выборке по срезу [:] создается новая копия списка, поэтому именно так обычно список и копируют
  • Срез порождает новый список или кортеж, но для каждого выбранного элемента копируется только ссылка

Присваивание срезу

В отличие от строк и кортежей списки могут изменяться по месту. Одним из вариантов модификации является присваивание срезу. Срезу с указанным шагом можно присвоить список, содержащий соответствующее количество новых элементов:

l = [1, 2, 3, 4, 5, 6]  l[::2] = [0, 0, 0]  print(l)  # => [0, 2, 0, 4, 0, 6]  

Если вы попробуете присвоить срезу с шагом неверное количество элементов, то получите ошибку:

l = [1, 2, 3, 4]  l[::2] = [5, 6, 7]  # Traceback (most recent call last):  #   File "<stdin>", line 1, in <module>  # ValueError: attempt to assign sequence of size 3 to extended slice of size 2  

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

l = [1, 2, 3]  l[2:] = [4, 5]  print(l)  # => [1, 2, 4, 5]  l[1:-1] = [100]  print(l)  # => [1, 100, 5]  l[:] = []  print(l)  # => []  

Сначала список растет, потом уменьшается, а под конец вообще становится пустым — и все с помощью компактного, но мощного синтаксиса срезов.

Срезы-значения

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

Значение среза можно сконструировать с помощью функции slice:

first_two = slice(2)  each_odd = slice(None, None, 2)  print(each_odd)  # => slice(None, None, 2)  l = [1, 2, 3, 4, 5]  print(l[first_two])  # => [1, 2]  print(l[each_odd])  # => [1, 3, 5]  

Функция slice принимает от одного до трех параметров — те самые STARTSTOP и STEP. При вызове функции с одним параметром, функция вызывается с параметром STOP.

Если вы хотите пропустить один из параметров, то подставьте вместо него None. Также None можно использовать и в записи срезов в квадратных скобках — там он так же будет означать пропуск значения.

На месте параметров среза могут быть любые выражения, лишь бы эти выражения вычислялись в целые числа или None.

Соотношение START и STOP

В срезе элемент с индексом STOP не попадает в выборку, в отличие от элемента с индексом START.

У такого поведения есть одна особенность. Какой бы неотрицательный индекс n мы ни выбрали, для любого списка будет соблюдаться указанное равенство:

l == l[:n] + l[n:]  

Посмотрим на такой пример:

s = 'Hello!'  print(s[:2] + s[2:])  # => 'Hello!'  print(s[:4] + s[4:])  # => 'Hello!'  print(s[:0] + s[0:] == s)  # => True  print(s[:100] + s[100:] == s)  # => True

Вернуться:

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