Книга: Как устроен Python. Гид для разработчиков, программистов и интересующихся
Назад: 17. Функции
Дальше: 19. Операции ввода/вывода с файлами

18. Индексирование и срезы

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

18.1. Индексирование

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

>>> my_pets = ["dog", "cat", "bird"]

>>> my_pets[0]

'dog'

СОВЕТ

Напомним, что в Python индексирование начинается с 0. Чтобы извлечь первый элемент, используйте индекс 0, а не 1.

В Python предусмотрена удобная возможность обращения к элементам по отрицательным индексам. Индекс –1 обозначает последний элемент, –2 — предпоследний и т.д. Эта запись чаще всего используется для получения последнего элемента списка:

>>> my_pets[-1]

'bird'

Гвидо ван Россум, создатель Python, в своем твите объяснил, как следует понимать отрицательные значения индексов:

«…Правильный подход [к отрицательному индексированию] — интерпретировать a[-X] как a[len(a)-X]»

@gvanrossum

Операции индексирования также можно выполнять с кортежами и строками:

>>> ('Fred', 23, 'Senior')[1]

23

 

>>> 'Fred'[0]

'F'

Некоторые типы, например множества, не поддерживают операции индексирования. Если вы хотите определить собственный класс, поддерживающий операции индексирования, реализуйте метод .__getitem__.

514729.png 

Рис. 18.1. Положительные и отрицательные значения индексов

18.2. Срезы

Кроме извлечения одного элемента по целочисленному индексу вы можете воспользоваться срезом (slice) для извлечения подпоследовательности. Срез может содержать начальный индекс, необязательный конечный индекс и необязательное приращение (все значения разделяются двоеточиями).

Срез для извлечения первых двух элементов списка:

>>> my_pets = ["dog", "cat", "bird"] # список

>>> print(my_pets[0:2])

['dog', 'cat']

Напомним, что в Python используются полуоткрытые интервалы. Список доходит до конечного индекса, но не включает его. Как упоминалось ранее, функция range также аналогично ведет себя со вторым параметром.

514743.png 

Рис. 18.2. Выделение первых четырех символов строки. Показаны три варианта; предпочтительным считается последний. Нулевой индекс не указан, так как он используется по умолчанию. Решение с отрицательными индексами выглядит просто глупо. Также продемонстрирован срез трех последних символов. И снова последний вариант считается идиоматическим решением. Первые два варианта предполагают, что длина строки равна 8 символам, а последний будет работать с любой строкой, содержащей не менее 3 символов

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

>>> print(my_pets[:2])

['dog', 'cat']

В срезах также могут использоваться отрицательные индексы. Отрицательной может быть как начальная, так и конечная позиция. Индекс –1 представляет последний элемент. Если срез распространяется до последнего элемента, вы получите все, кроме этого элемента:

>>> my_pets[0:-1]

['dog', 'cat']

>>> my_pets[:-1] # defaults to 0

['dog', 'cat']

 

>>> my_pets[0:-2]

['dog']

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

>>> my_pets[1:]

['cat', 'bird']

>>> my_pets[-2:]

['cat', 'bird']

Наконец, для начального и конечного индекса могут использоваться значения по умолчанию. Если оба индекса отсутствуют, то возвращаемый срез проходит от начала до конца (и содержит копию списка). Эта конструкция может использоваться для быстрого копирования списков в Python:

>>> print(my_pets[:])

['dog', 'cat', 'bird']

18.3. Приращения в срезах

После начального и конечного индекса срез также может получать приращение. Если приращение не задано, по умолчанию используется значение 1. Приращение 1 означает, что из последовательности извлекается каждый элемент между индексами. С приращением 2 берется каждый второй элемент, с приращением 3 — каждый третий и т.д.:

>>> my_pets = ["dog", "cat", "bird"]

>>> dog_and_bird = my_pets[0:3:2]

>>> print(dog_and_bird)

['dog', 'bird']

 

>>> zero_three_six = [0, 1, 2, 3, 4, 5, 6][::3]

>>> print(zero_three_six)

[0, 3, 6]

ПРИМЕЧАНИЕ

Функция range также поддерживает третий параметр, задающий приращение:

>>> list(range(0, 7, 3))

[0, 3, 6]

 

Приращение может быть отрицательным. Приращение –1 означает, что вы двигаетесь в обратном направлении справа налево. Чтобы использовать отрицательное приращение, укажите значение начального индекса больше конечного. Исключением является ситуация, в которой опущен как начальный, так и конечный индекс: приращение –1 переставляет элементы последовательности в обратном порядке:

>>> my_pets[0:2:-1]

[]

>>> my_pets[2:0:-1]

['bird', 'cat']

 

>>> print([1, 2, 3, 4][::-1])

[4, 3, 2, 1]

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

>>> 'emerih'[::-1]

'hireme'

Конечно, от вас, скорее всего, потребуют сделать это на C. Просто скажите, что вы хотите программировать на Python!

18.4. Итоги

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

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

В Python предусмотрена удобная возможность использования отрицательных значений для индексирования или создания среза относительно конца последовательности. Это позволяет выполнять операции относительно длины последовательности, так что вам не приходится беспокоиться о вычислении длины и вычитании смещений из полученного результата.

18.5. Упражнения

1. Создайте переменную с вашим именем, хранящимся в формате строки. Используйте операции индексирования для получения первого символа. Извлеките последний символ. Будет ли ваш код для извлечения последнего символа работать с именем произвольной длины?

2. Создайте переменную filename. Предполагая, что за именем файла следует трехбуквенное расширение, найдите расширение с использованием операции среза. Так, для файла README.txt должно быть получено расширение txt. Будет ли ваш код работать с именами файлов произвольной длины?

3. Создайте функцию is_palindrome для проверки того, что переданное слово одинаково читается в обоих направлениях.

Назад: 17. Функции
Дальше: 19. Операции ввода/вывода с файлами