Всё, что Вы хотели знать о слайсах

    Маленькое вступление. Уверен, что каждый, кто использовал питон некоторое время, полюбил выражения в прямоугольных скобочках. В этой статье я хочу от «а» до «я» рассказать о срезах. Для начала немного о терминологии: в английском языке их называют «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: Спасибо за комментарии, исправился, дополнил.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 34
    • 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
                        Много времени прошло, надеюсь Вы еще на сайте. Сможете ответить, что тут будет выведено?
                        arr = [range(1, 20)]
                        print(arr[0:len(arr)-1:-1])
                        print(arr[0:len(arr)-2:-1])
                        print(arr[0:len(arr)-3:-1])
                        
                        • 0

                          Как-то умышленно запутано. Зачем писать такой код?
                          Весь "трюк" в том что len(arr) = 1 ( arr будет [ [ 1, 2, ... 19 ] ]). Учитывая содержания этого треда, дальше все понятно, Но лучше такой код просто не писать.

                          • +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
                                  уф… Это была часть курса «моделирование»…

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