Индексы списков и срез в Python

Базовый срез

Для любого итерируемого объекта (например, строки, списка и т. Д.) Python позволяет срезать и вернуть подстроку или подсписок своих данных.

Формат для среза:

 iterable_name[start:stop:step]
 

где,

  • start первый индекс среза. По умолчанию 0 (индекс первого элемента)
  • stop последний индекс среза. По умолчанию len (индекс последнего элемента)
  • step шаг размера среза.(лучше объясняется в примере ниже)

Примеры:

a = "abcdef"
a             # так же как a[:] или a[::] если используются индексы по умолчанию всех трех индексов

>>>Out: 'abcdef'

a[-1]        

>>>Out: 'f'

a[:]         

>>>Out: 'abcdef'

a[::]

>>>Out: 'abcdef'

a[3:] 		# от элемента с индексом 3 до конца(размер шага имеет значение по умолчанию)

>>>Out: 'def'

a[:4]        # от начала (по умолчанию 0) до позиции 4 (не включая ее)

>>>Out: 'abcd' 


a[2:4]       # от позиции 2 до позиции 4 (не включая ее)

>>>Out: 'cd'

Кроме того, любой из вышеперечисленных срезов может использоваться с определенным размером шага:

a[::2]       # каждый второй элемент

>>>Out: 'ace'

a[1:4:2]     # от индекса 1 до индекса 4 (исключая), каждый второй элемент)

>>>Out: 'bd'

Индексы могут быть отрицательными, и в этом случае они вычисляются с конца последовательности

a[:-1]     #от нулевого индекса (по умолчанию) до 2ого индекса с конца (последний элемент - 1)

>>>Out: 'abcde' 

a[:-2]     # от нулевого индекса (по умолчанию) до 3его индекса с конца (последний элемент - 2)

>>>Out: 'abcd' 

a[-1:]     # от последнего элемента до конца (по умолчанию len()) 

>>>Out: 'f'

Размер шага также может быть отрицательным, в этом случае срез будет перебирать список в обратном порядке:

 a[3:1:-1]  # от индекса 2 до None (по умолчанию), в обратном порядке)
 
 >>>Out: 'dc'

Эта конструкция полезна для обращения итеративного

a[::-1]     # от последнего элемента (по умолчанию len()-1), до первого в обратном порядке (-1))

 >>>Out: 'fedcba'

Обратите внимание на то, что для отрицательных шагов по умолчанию end_index не None

a[5:None:-1] # эквивалентно a[::-1]

>>>Out: 'fedcba'

a[5:0:-1]    # от последнего элемента (индекс 5) до второго элемента (индекс 1)

>>>Out: 'fedcb'

Создание мелкой копии массива

Быстрый способ сделать копию массива (в отличие от присвоения переменной с другой ссылкой на исходный массив):

 arr[:]

 

Давайте рассмотрим синтаксис. [:] Означает , что start , end , и slice все опущены. Они по умолчанию равны 0 , len(arr) , и 1 , соответственно, что означает, что мы запрашиваем не будет иметь все элементы arr от начала до самого конца. На практике это выглядит примерно так:

arr = ['a', 'b', 'c']
copy = arr[:]
arr.append('d')
print(arr)

>>>Out: ['a', 'b', 'c', 'd']

print(copy)

>>>Out: ['a', 'b', 'c']

 

Как вы можете видеть, arr.append('d') добавил d к arr, но copy осталась неизменной!

Обратите внимание, что это делает неполную копию, и copy не идентичен arr.copy().

Разворот объекта

Вы можете использовать срезы для легкого разворота str , list или tuple (или в основном любой набор объектов , который реализует срез с параметром шага). Вот пример разворота строки, хотя это в равной степени относится и к другим типам, перечисленным выше:

s = 'reverse me!'
s[::-1]

>>>Out: '!em esrever'

Давайте быстро посмотрим на синтаксис. [::-1] означает , что срез должен быть с самого начала до конца строки (потому что start и end опущены) и шаг -1 означает , что он должен двигаться через колонну в обратном направлении.

Индексирование пользовательских классов: __getitem__, __setitem__ и __delitem__

class MultiIndexingList:
    def __init__(self, value):
        self.value = value

    def __repr__(self):
        return repr(self.value)

    def __getitem__(self, item):
        if isinstance(item, (int, slice)):
            return self.__class__(self.value[item])
        return [self.value[i] for i in item]

    def __setitem__(self, item, value):
        if isinstance(item, int):
            self.value[item] = value
        elif isinstance(item, slice):
            raise ValueError('Невозможно интерпретировать срез с множественным индексированием')
        else:
            for i in item:
                if isinstance(i, slice):
                    raise ValueError('Невозможно интерпретировать срез с множественным индексированием')
                self.value[i] = value

    def __delitem__(self, item):
        if isinstance(item, int):
            del self.value[item]
        elif isinstance(item, slice):
            del self.value[item]
        else:
            if any(isinstance(elem, slice) for elem in item):
                raise ValueError('Невозможно интерпретировать срез с множественным индексированием')
            item = sorted(item, reverse=True)
            for elem in item:
                del self.value[elem]

Это позволяет нарезать и индексировать для доступа к элементу:

a = MultiIndexingList([1,2,3,4,5,6,7,8])
a
>>>Out: [1, 2, 3, 4, 5, 6, 7, 8]

a[1,5,2,6,1]

>>>Out: [2, 6, 3, 7, 2]

a[4, 1, 5:, 2, ::2]

>>>Out: [5, 2, [6, 7, 8], 3, [1, 3, 5, 7]]


#       4|1-|----5:---|2-|-----::2-----   <-- определяет какой элемент пришел из какой позиции

Во время установки и удаления элементов допускает только запятые целочисленной индексации (без нарезки):

a[4] = 1000
a

>>>Out: [1, 2, 3, 4, 1000, 6, 7, 8]

a[2,6,1] = 100

>>>Out: [1, 100, 100, 4, 1000, 6, 100, 8]

del a[5]
a

>>>Out: [1, 100, 100, 4, 1000, 100, 8]

del a[4,2,5]
a

>>>Out: [1, 100, 4, 8] 

Назначение среза

Еще одна полезная функция, использующая срезы- это назначение срезов. Python позволяет назначать новые фрагменты для замены старых фрагментов списка за одну операцию.

Это означает, что если у вас есть список, вы можете заменить несколько членов в одном назначении:

lst = [1, 2, 3]
lst[1:3] = [4, 5]
print(lst)

>>>Out: [1, 4, 5]

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

lst = [1, 2, 3, 4, 5]
lst[1:4] = [6]
print(lst)

>>>Out: [1, 6, 5]

Также возможно использовать известный синтаксис среза для таких вещей, как замена всего списка:

lst = [1, 2, 3]
lst[:] = [4, 5, 6]
print(lst) 

>>>Out: [4, 5, 6]

Или только два последних члена:

lst = [1, 2, 3]
lst[-2:] = [4, 5, 6]
print(lst)

>>>Out: [1, 4, 5, 6]

Базовое индексирование

В списках Python первый элемент в списке можно получить по индексу 0

arr = ['a', 'b', 'c', 'd']
print(arr[0])

>>>Out: 'a'

Вы можете получить доступ к второму элементу в списке по индексу 1 , третий элемент по индексу 2 и так далее:

print(arr[1])

>>>Out: 'b'

print(arr[2])

>>>Out: 'c'

Вы также можете использовать отрицательные индексы для доступа к элементам в конце списка. например. индекс -1 даст вам последний элемент списка и индекс -2 даст вам второй до последнего элемента списка:

print(arr[-1])

>>>Out: 'd'

print(arr[-2])

>>>Out: 'c'

Если вы пытаетесь получить доступ к индексу , которого нет в списке, будет вызвана ошибка IndexError:

print arr[6]

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range