Пользователь
0,1
рейтинг
31 марта 2010 в 15:15

Разработка → Всё, что Вы хотели знать о слайсах

Маленькое вступление. Уверен, что каждый, кто использовал питон некоторое время, полюбил выражения в прямоугольных скобочках. В этой статье я хочу от «а» до «я» рассказать о срезах. Для начала немного о терминологии: в английском языке их называют «slice». Я буду называть их то «слайсами», то «срезами», как в моем понимании этого слова. Будем все учиться на примерах. Для меня, такой метод был бы самым удобным, быстрым и простым.
Для начала, самое распространенное применение. Создания копии последовательности или ее части.
Рассмотрим срез как часть последовательности. Например, несколько срезов со списка:
>>> s = [1, 2, 3, 4, 6] #простой список
>>> s[:] #копия списка, часто очень полезно
[1, 2, 3, 4, 6]
>>> s[1:] # все элементы кроме первого
[2, 3, 4, 6]
>>> s[-3:] # последние 3 элемента
[3, 4, 6]
>>> s[2:-2] #откидываем первые и последние 2
[3]

Это ещё не все,
Далеко не все знают, но могут быть слайсы с тремя параметрами:
>>> s[::2] #парные элементы
[1, 3, 6]
>>> s[1:4:2] #элементы с первого по четвертый с шагом 2
[2, 4]

Все эти действия можно проворачивать со строками, кортежами и списками.
>>> "Hello Dolly!"[1::2]
'el ol!'

Совсем забыл, спасибо xeningem:
>>> "God saw I was dog"[::-1]
'god saw I was doG'
>>> #отрицательный шаг может оказаться граблями, если не знать особенностей. См комментарии.

Но и это ещё не все, есть несколько действий со срезами, которые можно делать только со списками (ну, почти). Дело в том, что они единственные из базовых последовательностей, которые могут изменяться, и, для которых, имеет значение порядок (нельзя делать срезы из словарей и множеств/наборов). Дальше пойдет разговор о срезах, которые изменяют последовательность.
Слайсы можно удалять, например:
>>> s = list(range(10)) #заполняем 0..9
>>> del s[3: -2: 2] #удаляем элементы между третьим и предпоследним с шагом 2
>>> s
[ 0, 1, 2, 4, 6, 8, 9]

Ещё можно вставлять элементы:
В варианте замены:
>>> s[2:5:2]=list("AF") #список был [0, 1, 2, 4, 6, 8, 9], мы заменили указанные элементы на [‘A’,’F’]
>>> #да, ещё в такой конструкции должны совпадать размеры, это легче понять попробовав
>>> s
[ 0, 1, 'A', 4, 'F', 8, 9]

Ну, или вариант вставки попроще:
>>> s[3:4] = ["4 was here"] # замена последовательного кусочка
>>> s
[ 0, 1, 'A', '4 was here', 'F', 8, 9]
>>> s[1:1] = ["after zero"] #или, просто, вставка
>>> s
[ 0, 'after zero', 1, 'A', '4 was here', 'F', 8, 9]

Если мы хотим создать класс, с которого можно снимать срезы? Проще некуда, для этого есть два пути:
Неправильный:
1) Переопределить фунции __getslice__, __delslice__ и __setslice__. Но это устаревший метод (в 2.0 помечен как deprecated)
И правильный:
2) Переопределить __getitem__, __setitem__ и __delitem__.
С первого взгляда все кажется предельно простым, но если присмотреться, то __getitem__(self, key) – получает только один параметр, key, а у среза у нас может быть целых 3 числа… Все предельно просто: в случае, когда кто-то пытается срезать кусочек от нашего объекта, значением key функция получит объект типа slice:
>>> class MySliceble():
    def __getitem__(self, key):
        if isinstance(key, slice):
            return list(range(key.start, key.stop, key.step))
        else:
            raise Exception("Trying to access by index")
>>> my = MySliceble()
>>> my[2:10:1]
[2, 3, 4, 5, 6, 7, 8, 9]

Конечно, пример очень символический, но понять можно: у объекта класса slice есть три свойства: start, stop и step, соответствуют числам из скобок среза. Нужно быть внимательным, если число пропущена, то значение будет None, например [::] будет slice(None, None, None) а [:-3] будет slice(None, -3, None).
Замену/вставку и удаление срезов делаем по аналогии.
Как упражнение, можете попробовать перегрузить словарь, чтобы с него можно было делать срезы. В питоне3 это будет начинаться как class SliceableDict(dict):
Ну, вроде все, что есть о срезах.
Если что пропустил с удовольствием выучу и допишу.

Upd1: накопил 5 кармы, перенес в питоний блог. Спасибо.
Upd2: Спасибо за комментарии, исправился, дополнил.
Вовка aka Shchvova @Shchvova
карма
51,8
рейтинг 0,1
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (31)

  • 0
    Просто отлично. Большое спасибо.
    • 0
      Пожалуйста :)
      очень приятно что это было интересно не только мне…
      • 0
        Читаю книгу по питону. Про срезы всё расписано в третей главе.
        • 0
          Какая книга? Например, в любимой Dive Into Python ничего нету про срезы с шагом, или как их перегружать.
          • 0
            Оператор получения среза имеет три формы записи:
            seq[start]
            seq[start:end]
            seq[start;end:step]

            Саммерфилд — Программирование на Python 3 / 2009 / стр. 90

            Про перегрузку ничего не скажу, так далеко не дошёл :) Но думаю далее это будет рассказано на ряду с перегрузкой операторов.
            • 0
              Спасибо! О, можно заказать в библиотеке :)
              Пойду читать.

              З.Ы. Книга книге рознь.
  • 0
    полезно, спасибо… про классы до вас как-то не задумывался.
  • 0
    Обнаружил, что знаю эти фишки ровно через одну :) т.е. «самое распространенное применение», «можно проворачивать со строками, кортежами и списками» и «можно вставлять элементы»

    Спасибо за пост.
  • 0
    Сильно.

    но могут быт слайсы с трема параметрами
    • 0
      А за статью — спасибо. О параметре step не знал.
  • 0
    >> list(range(
    видимо примеры под 3й питон, стоит пометить.

    >> перенес в питой блог.
    очепяточка.
    • 0
      Спасибо!
      list(range( — чтобы работало во всех питонах. Это ведь не ман по созданию списков :)
      Во втором, создаст копию с рейнджа.
  • +2
    Спасибо, интересно. Один из часто задаваемых вопросов — как «развернуть» строку(кортеж, список):
    >>>s = «Hello Dolly!»[::-1]
    >>>print s
    !ylloD ,olleH

    Прекрасно, я считаю — интуитивно понятно, коротко и ясно.
    • +1
      Нет, не спрашивайте меня, откуда взялась запятая :) Это не подтасовка, это опечатка.
      • 0
        Ну вот, с самого утра у меня день будет веселым :)
        Спасибо!
    • 0
      Да, ещё, дума, стоит ли написать, что тут есть маленький подводный камешек:
      Copy Source | Copy HTML
      1. >>> s = "Hello Dolly!"
      2. >>> s[ 0:len(s):-1]
      3. ''
      4. >>> #то была пустая строка
      5. >>> s[ 0:len(s)]
      6. 'Hello Dolly!'
      • 0
        Да, камень большой, когда я сам начал разбираться со срезами я наверное полчаса потратил пока не нашёл нужный вариант. О, эти прекрасные s[:-len(s)-1:-1], s[len(s):-len(s)-1:-1] и т.д. :) В конце концов внимательно(!) перечитал документацию, подумал, и немного продвинулся в понимании принципов языка.
        Ожидаемо? Да. Привычно? Уже тоже да.
  • 0
    Не очень понятно — неужели кто-то этого не знал? А если не знал, то как можно учить язык ни разу не прочитав доку по нему?
    • 0
      Если бы все доку читали, то подобные статьи были бы ненужны. Да и вообще, большинство книг тоже. Оно конечно есть в доках, там просто сильно разсредоточено. Или это сарказм?
      • +2
        Да какой сарказм? Просто я так учу языки — читаю доку, чтобы представлять что в языке есть, потом уже пытаюсь программировать.
        • +3
          Ваш метод очень хороший. Я начал питон с guide в доках. Но потом прочитал книжку, Dive Into Python. Если чесно, то для меня основным источником для новых языков есть книги. Думаю, таких людей очень много.
  • 0
    Никогда не используйте в качестве имени переменной одиночные буквы «l», «I», «O» (PEP 8, правила именования)
    • +1
      Хорошее замечание. Я об этом думал… Пойду переименую на =)
    • 0
      Done!
  • +1
    Когда читаю такие статьи — понимаю как люди не любят читать документацию, даже если она небольшая и понятная.
    • +1
      документация и учебник — разные вещи. Для меня документация ето прежде всего место где можно почитать про вещи которые о существовании которых я знаю, но самих их я не знаю. Как учебник, она не слишком хороша, особенно в местах, где просто перечень фукнций, и что они делают.
      Например, как узнать о возможности срезов с шагом, просто читая документацию, ту ее часть, что не reference?
      • 0
        Хм, а разве language reference — не часть документации? Я вот считаю, что Tutorial-ы — это не совсем документация, а больше обзор самого необходимого… Про слайсы и работу с ними в классах говорится в 3-ей главе language reference (Data Model) — кстати, очень толково написано — советую.
        За статью спасибо — из нового для себя вынес такой неочевидный способ обращения строки (хотя всё равно применяю ''.join(reversed('Hello Dolly')), но две закрывающиеся скобки навевают грусть и печаль).
  • 0
    Такие срезы и их синтаксис позаимствовали из Матлаба, похоже. Ну и хорошо, удобно ведь.
    • 0
      вспомнил MatLab, аж передернуло. Как же плохо нам его преподавали… Кстате, по запросу «matlab pyhon» гугл всякого интересного выдает. Сижу, читаю.
      • 0
        А зачем вас учили самому матлабу, неужели есть такой предмет? Мне преподовали прикладную математику и обработку сигналов, а задания надо было частично делать на матлабе, все его сами учили и друг у друга :)
        • 0
          уф… Это была часть курса «моделирование»…

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