Срезы в Python: индексация, отрицательные индексы и шаг — понятное руководство с...

Срезы в Python: индексация, отрицательные индексы и шаг — понятное руководство с примерами

Срезы в Python: индексация, отрицательные индексы и шаг — понятное руководство с примерами

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

Базовый синтаксис срезов

Общий вид: s[start:end:step]. Интервал полуоткрытый: start включительно, end не включительно. step — шаг, по умолчанию 1.

nums = [10, 20, 30, 40, 50, 60]
print(nums[1:4])      # [20, 30, 40]
print(nums[:3])       # [10, 20, 30]  (start по умолчанию = 0)
print(nums[3:])       # [40, 50, 60]  (end по умолчанию = len(nums))
print(nums[:])        # копия списка [10, 20, 30, 40, 50, 60]
print("hello"[1:4])  # "ell"

Важно: выход за границы срезом ошибок не вызывает — Python просто обрежет результат.

Отрицательные индексы и шаг

Отрицательные индексы считают с конца: -1 — последний элемент, -2 — предпоследний и т.д. Шаг может быть отрицательным — это удобно для разворота последовательностей и выборки справа налево.

nums = [10, 20, 30, 40, 50, 60]
print(nums[-3:])      # [40, 50, 60]
print(nums[:-2])      # [10, 20, 30, 40]
print(nums[::-1])     # [60, 50, 40, 30, 20, 10] — разворот
print(nums[5:1:-2])   # [60, 40] — справа налево с шагом 2

Шаг не может быть 0: nums[::0] выдаст ValueError.

Почему срезы безопаснее индексов

Доступ по индексу может вызвать IndexError, если индекс вне диапазона. Срезы же возвращают пустую последовательность, что безопаснее в пайплайнах обработки данных.

s = [1, 2, 3]
# print(s[10])  # IndexError
print(s[10:11])  # [] — безопасно

Изменение списков через срезы

Главная мощь срезов — в присваивании и удалении участков списка. Это позволяет вставлять, заменять и удалять блоки элементов за одну операцию.

# Замена участка
nums = [10, 20, 30, 40, 50]
nums[1:4] = [21, 31, 41]
print(nums)  # [10, 21, 31, 41, 50]

# Вставка без замены (срез пустой)
nums = [1, 2, 5]
nums[2:2] = [3, 4]
print(nums)  # [1, 2, 3, 4, 5]

# Удаление участка через пустой список
nums = [1, 2, 3, 4, 5]
nums[1:4] = []
print(nums)  # [1, 5]

# Очистка списка срезом
nums = [1, 2, 3]
nums[:] = []
print(nums)  # []

# Удаление через del
nums = [10, 20, 30, 40]
del nums[1:3]
print(nums)  # [10, 40]

Присваивание с шагом тоже возможно, но длины должны совпадать:

nums = [0, 1, 2, 3, 4, 5]
nums[::2] = [10, 20, 30]  # заменим элементы на чётных позициях
print(nums)  # [10, 1, 20, 3, 30, 5]

# nums[::2] = [1, 2]  # ValueError: attempt to assign sequence of size 2 to extended slice of size 3

Копирование с помощью срезов

s[:] создаёт поверхностную копию списка. Аналоги: list(s), s.copy(). Для вложенных структур это не глубокая копия — внутренние объекты остаются общими.

a = [[1], [2]]
b = a[:]           # поверхностная копия
b[0].append(99)
print(a)  # [[1, 99], [2]] — изменился и исходник

# Для глубокой копии используйте copy.deepcopy
import copy
c = copy.deepcopy(a)
c[0].append(100)
print(a)  # [[1, 99], [2]]
print(c)  # [[1, 99, 100], [2]]

Объект slice и переиспользование шаблонов

Синтаксис [start:end:step] — это сахар для встроенного объекта slice. Его удобно создавать заранее и применять многократно.

mid3 = slice(2, 5)      # эквивалент [2:5]
rev2 = slice(None, None, -2)  # эквивалент [::-2]

arr = list(range(10))
print(arr[mid3])  # [2, 3, 4]
print(arr[rev2])  # [9, 7, 5, 3, 1]

Когда лучше itertools.islice

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

from itertools import islice

def gen():
    n = 0
    while True:
        yield n
        n += 1

first_five = list(islice(gen(), 5))
print(first_five)  # [0, 1, 2, 3, 4]

Производительность и память

  • Срез возвращает новую последовательность — это O(k) по времени и памяти, где k — размер среза.
  • Избегайте лишних копий на больших данных: например, не делайте s[::-1], если можно пройти по reversed(s) без создания копии.
  • Частое присваивание больших срезов может быть дороже, чем один раз создать новый список нужной формы.
  • data = list(range(10_000_000))
    # Быстрее по памяти: просто итерироваться по reversed(data)
    for x in reversed(data):
        pass
    # Медленнее по памяти: создаёт копию
    # rev = data[::-1]
    

    Частые ошибки и как их избежать

  • Ожидание включительности верхней границы: помните, end не включается. Нужно «включить» элемент с индексом r? Пишите [:r+1].
  • -0 — это просто 0. Срез arr[-0:]
    равен arr[0:].
  • Шаг 0 запрещён: используйте 1, -1, 2 и т.д.
  • Строки неизменяемы: доступно только получение среза, не присваивание.
  • s = "python"
    # s[1:3] = "YY"  # TypeError: 'str' object does not support item assignment
    print(s[:3] + "-" + s[3:])  # py-thon
    

    Практические мини-задачи

    1. Развернуть строку и сделать каждую 2-ю букву заглавной.
    2. Удалить из списка каждый 3-й элемент.
    3. Вставить элементы после позиции i без замены существующих.
    # 1. Разворот и заглавные через шаг
    s = "abcdefg"
    rev = s[::-1]
    res = ''.join(ch.upper() if i % 2 else ch for i, ch in enumerate(rev))
    print(res)  # gFeDcBa
    
    # 2. Удалить каждый 3-й элемент (индексы 2, 5, 8, ...)
    arr = list(range(1, 13))
    del arr[2::3]
    print(arr)  # [1, 2, 4, 5, 7, 8, 10, 11]
    
    # 3. Вставка без замены на позицию i
    arr = [1, 2, 5]
    i = 2
    arr[i:i] = [3, 4]
    print(arr)  # [1, 2, 3, 4, 5]
    

    Итоги

    Срезы в Python — это мощный, лаконичный и безопасный механизм работы с последовательностями. Освойте базовый синтаксис, отрицательные индексы и присваивание срезов — и вы заметите, насколько чище и короче станет ваш код.

    Если хотите закрепить навыки на практике и системно пройти путь от основ к уверенной разработке, рекомендую курс: Освоить Python на практике: с нуля до уровня «гуру».

    Источник

    НЕТ КОММЕНТАРИЕВ

    Оставить комментарий