Работать с одиночными элементами вы уже умеете. Настало время перейти к очень интересному инструменту, который Python предоставляет для работы с целыми подмножествами элементов списка - срезы (slices).
Синтаксис срезов
Срез записывается так же, как записывается обращение к элементу списка по индексу:
some_list[START:STOP:STEP]
У среза три параметра:
START
— индекс первого элемента в выборкеSTOP
— индекс элемента списка, перед которым срез должен закончиться. Сам элемент с индексомSTOP
не будет входить в выборкуSTEP
— шаг выбираемых индексов
l = [1, 2, 3, 4, 5]
# Срез от нулевого по третий индекс с шагом 1
slice = l[0:3:1]
print(slice) # => [1, 2, 3]
# Срез от нулевого по третий индекс с шагом 2
slice = l[0:3:2]
print(slice) # => [1, 3]
При этом любой из трех параметров среза может быть пропущен и вместо соответствующего параметра будет значение по умолчанию:
- По умолчанию
START
означает «от начала списка». - По умолчанию
STOP
означает «до конца списка включительно». - По умолчанию
STEP
означает «брать каждый элемент».
Вот несколько примеров с разными наборами параметров:
[:]
или[::]
— весь список.[::2]
— нечетные по порядку элементы.[1::2]
— четные по порядку элементы.[::-1]
— все элементы в обратном порядке.[5:]
— все элементы, начиная с шестого.[:5]
— все элементы, не доходя до шестого.[-2:1:-1]
— все элементы от предпоследнего до третьего в обратном порядке. Во всех случаях выборки от большего индекса к меньшему нужно указывать шаг.
Теперь разберем как можно использовать срезы.
Выборка элементов
Срезы работают не только со списками, но и с кортежами, и даже со строками. Результатом применения выборки всегда становится новое значение соответствующего типа — список, кортеж, строка:
word = 'hello'
print(word[2:]) # => llo
user_data = ('Ivan', 'ivan@example.com', 'student',)
print(user_data[1::]) # ('ivan@example.com', 'student')
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(numbers[1::2]) # [2, 4, 6, 8, 10]
Присваивание срезу
В отличие от строк и кортежей списки могут изменяться. Одним из вариантов модификации является присваивание срезу. Срезу с указанным шагом можно присвоить список из новых элементов:
l = [1, 2, 3, 4, 5, 6]
l[::2] = [0, 0, 0]
print(l) # => [0, 2, 0, 4, 0, 6]
Срез [::2]
означает, что мы выбираем элементы с шагом 2, то есть берем каждый второй элемент. Список [0, 0, 0]
- это список, который мы присваиваем выбранным элементам. Срез l[::2]
теперь содержит элементы с индексами 0, 2 и 4 (т.е. 1, 3 и 5). Присваивание [0, 0, 0]
этим элементам заменяет их на нули. В результате изменения списка 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()
принимает от одного до трех параметров — те самые START
, STOP
и STEP
. При вызове функции с одним параметром, функция вызывается с параметром STOP
.
Если вы хотите пропустить один из параметров, то подставьте вместо него None
. Также None
можно использовать и в записи срезов в квадратных скобках — там он так же будет означать пропуск значения.
На месте параметров среза могут быть любые выражения, лишь бы эти выражения вычислялись в целые числа или None
.
Соотношение START
и STOP
В срезе элемент с индексом STOP
не включается в результат, в отличие от элемента с индексом START
.
Эту особенность можно использовать, какой бы неотрицательный индекс n
мы ни выбрали, для любого списка будет соблюдаться указанное равенство:
l = [1, 2, 3, 4, 5]
n = 2
l == l[:n] + l[n:] # True
Посмотрим на такой пример:
word = 'Hello!'
print(word[:2] + word[2:]) # => 'Hello!'
print(word[:4] + word[4:]) # => 'Hello!'
print(word[:0] + word[0:] == word) # => True
print(word[:100] + word[100:] == word) # => True
Это свойство удобно использовать, когда вы разбираете некий текст. Вам достаточно двигать позицию разрезания строки на начало и остаток, не заботясь, что какая-либо информация на границе разрезания потеряется.
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.