Pull to refresh

Срезы в Python

Python *
Sandbox

Вступление

К вашему вниманию гайд по слайсингу (AKA суши-оператор, слайс, срез)! Наверно, все пишущие на питоне знают о синтаксисе вырезания частей последовательности - s[a:b]. Сейчас же мы рассмотрим как самые очевидные, так и менее известные факты об операции получения среза.

(Но прежде чем начнём, нужно уточнить, что в Python как и во многих других языках последний элемент не включается в срезы и диапазоны, что соответствует индексации с нуля. seq[начало:конец:шаг] (Для вычисления данного выражения Python вызывает seq.__getitem__) - берёт срез от НАЧАЛО, до КОНЕЦ (не включая его), с шагом ШАГ. По умолчанию НАЧАЛО = 0, КОНЕЦ = длине объекта, ШАГ = 1. Соответственно, какие-нибудь, а возможно и все (seq[:], поговорим об этом позже) параметры могут быть опущены).

Получение по срезу

"Python"

P

Y

T

H

O

N

0

1

2

3

4

5

-6

-5

-4

-3

-2

-1

>>> some_str = "Python"
>>> some_str[:1]  # Задаём конечным индексом 1, а т.к. конец не включается, мы получим первый элемент последовательности ('P').
'P'
>>> PYTH = slice(0, 4)  # Вместо того чтоб загромождать код вшитыми диапазонами, мы можем проименовать их (например для упрощения разбора накладной). 
>>> some_str[PYTH]
'Pyth'
>>> some_str[::2]  # Шагаем через букву.
'Pto'

Немного о NumPy

Оператор [] может принимать несколько индексов или срезов, разделённых запятыми a[m:n, k:l]. В основном это используется в NumPy для получения двумерного среза и a[i, j] для получения одного элемента двумерного массива. Соответственно для вычисления a[i, j] Python вызывает a.__getitem__((i, j)). Кстати, т.к. "..." (единственный экземпляр класса ellipsis) распознаётся как лексема, то мы его можем использовать так же и в срезах. Применение этому нашлось в том же NumPy для сокращённого создания среза двумерного массива. Детальнее - https://scipy.github.io/old-wiki/pages/Tentative_NumPy_Tutorial.

Удаление по срезу

Так же мы можем удалять элементы последовательности по срезу с учётом того, что она поддерживает удаление элементов (изменяема))). Приведу пример с рантайм классом SupportsDeletion.

>>> import typing

>>> @typing.runtime_checkable
>>> class SupportsDeletion(typing.Protocol):
>>>     def __delitem__(self, key: typing.Any) -> typing.Any:
>>>         ...

>>> for builtin_type in (list, dict, str, tuple):
>>>     print(builtin_type.__name__, "->", isinstance(builtin_type, SupportsDeletion))

list -> True
dict -> True
str -> False
tuple -> False
>>> seq = [1, 2, 3, 4]
>>> del seq[:3]  # Удаляем первых три элемента последовательности.
>>> seq
[4]

Лайфхаки :)

№1 Можно перевернуть последовательность, если запросить срез seq[::-1]

>>> seq = (1, 2, 3, 4, 5, 6)
>>> seq[::-1]
(6, 5, 4, 3, 2, 1)

№2 Как удалить все элементы списка с помощью слайса, не разрушая сам обьект-список? (Разумеется, тот же результат можно получить вызвав метод .clear() у списка, но мы же сейчас про слайсы говорим + его нет во второй версии питона).

>>> seq = [1, 2, 3, 4, 5, 6]
>>> id(seq)
2071031395200
>>> del seq[:]
>>> id(seq)
2071031395200
>>> seq
[]

№3 Помимо очистки списков, нарезку также можно использовать для замены всех элементов списка, не создавая новый объект-список. Это отличная сокращённая запись для очистки списка и затем повторного его заполнения вручную:

>>> seq = [1, 2, 3, 4, 5, 6, 7]
>>> original_list = seq
>>> seq[:] = [1, 2, 3]
>>> seq
[1, 2, 3]
>>> original_list
[1, 2, 3]
>>> original_list is seq
True


Приведённый выше пример кода заменил все элементы в seq, но не уничтожил и воссоздал список как таковой. По этой причине старые ссылки на оригинальный объект-список по-прежнему действительны.

№4 Создание мелких копий существующих списков:

>>> seq = [1, 2, 3, 4, 5, 6, 7]
>>> copied_list = seq[:]
>>> copied_list
[1, 2, 3, 4, 5, 6, 7]
>>> copied_list is seq
False

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

Если же вам необходимо продублировать абсолютно всё, включая и элементы, то необходимо создать глубокую копию списка (copy.deepcopy(x)). Для этой цели пригодится встроенный модуль в Python copy.

Ключевые выводы

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

Tags:
Hubs:
Total votes 30: ↑13 and ↓17 -4
Views 8.3K
Comments Comments 14