Вопрос-Ответ

How slicing in Python works

Как работает нарезка в Python

Как работает нотация срезов в Python? То есть: когда я пишу код типа a[x:y:z], a[:], a[::2] и т.д., как я могу понять, какие элементы попадают в срез?


Смотрите , почему верхняя граница среза и диапазона исключительна? чтобы узнать, почемуxs[0:2] == [xs[0], xs[1]], нет [..., xs[2]].

Смотрите раздел Создание нового списка, содержащего каждый N-й элемент в исходном списке для xs[::N].

Смотрите Как работает присваивание с срезами списка? чтобы узнать, что xs[0:2] = ["a", "b"] делает.

Переведено автоматически
Ответ 1

Синтаксис таков:

a[start:stop]  # items start through stop-1
a[start:] # items start through the rest of the array
a[:stop] # items from the beginning through stop-1
a[:] # a copy of the whole array

Также есть step значение, которое можно использовать с любым из вышеперечисленных:

a[start:stop:step] # start through not past stop, by step

Ключевой момент, который следует помнить, заключается в том, что :stop значение представляет собой первое значение, которого нет в выбранном срезе. Итак, разница между stop и start заключается в количестве выбранных элементов (если step по умолчанию равно 1).

Другая особенность заключается в том, что start or stop может быть отрицательным числом, что означает, что оно отсчитывается от конца массива, а не от начала. Итак:

a[-1]    # last item in the array
a[-2:] # last two items in the array
a[:-2] # everything except the last two items

Аналогично, step может быть отрицательным числом:

a[::-1]    # all items in the array, reversed
a[1::-1] # the first two items, reversed
a[:-3:-1] # the last two items, reversed
a[-3::-1] # everything except the last two items, reversed

Python благосклонен к программисту, если элементов меньше, чем вы запрашиваете. Например, если вы запрашиваете a[:-2] и a содержит только один элемент, вы получаете пустой список вместо ошибки. Иногда вы предпочитаете ошибку, поэтому вы должны знать, что это может произойти.

Связь с slice объектом

slice Объект может представлять операцию среза, т.е.:

a[start:stop:step]

эквивалентно:

a[slice(start, stop, step)]

Объекты среза также ведут себя немного по-разному в зависимости от количества аргументов, аналогично range(), т.е. Поддерживаются оба slice(stop) и slice(start, stop[, step]).
Чтобы пропустить указание данного аргумента, можно использовать None, так что, например, a[start:] эквивалентно a[slice(start, None)] или a[::-1] эквивалентно a[slice(None, None, -1)].

Хотя нотация на основе : очень полезна для простого среза, явное использование slice() объектов упрощает программную генерацию среза.

Ответ 2

В руководстве по Python рассказывается об этом (прокрутите немного вниз, пока не дойдете до части о нарезке).

Графическая диаграмма ASCII также полезна для запоминания того, как работают срезы:

 +---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5
-6 -5 -4 -3 -2 -1

Один из способов запомнить, как работают срезы, - представить индексы как указывающие между символами, с левым краем первого символа, пронумерованным 0. Тогда правый край последнего символа строки из n символов имеет индекс n.


Ответ 3

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

>>> x[:]                # [x[0],   x[1],          ..., x[-1]    ]
>>> x[low:] # [x[low], x[low+1], ..., x[-1] ]
>>> x[:high] # [x[0], x[1], ..., x[high-1]]
>>> x[low:high] # [x[low], x[low+1], ..., x[high-1]]
>>> x[::stride] # [x[0], x[stride], ..., x[-1] ]
>>> x[low::stride] # [x[low], x[low+stride], ..., x[-1] ]
>>> x[:high:stride] # [x[0], x[stride], ..., x[high-1]]
>>> x[low:high:stride] # [x[low], x[low+stride], ..., x[high-1]]

Конечно, если (high-low)%stride != 0, то конечная точка будет немного ниже, чем high-1.

Если stride значение отрицательное, порядок немного меняется, поскольку мы ведем обратный отсчет:

>>> x[::-stride]        # [x[-1],   x[-1-stride],   ..., x[0]    ]
>>> x[high::-stride] # [x[high], x[high-stride], ..., x[0] ]
>>> x[:low:-stride] # [x[-1], x[-1-stride], ..., x[low+1]]
>>> x[high:low:-stride] # [x[high], x[high-stride], ..., x[low+1]]

Расширенные срезы (с запятыми и многоточиями) в основном используются только специальными структурами данных (такими как NumPy); базовые последовательности их не поддерживают.

>>> class slicee:
... def __getitem__(self, item):
... return repr(item)
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'
Ответ 4

В приведенных выше ответах не обсуждается назначение среза. Чтобы понять назначение среза, полезно добавить еще одну концепцию в формат ASCII:

                +---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
Slice position: 0 1 2 3 4 5 6
Index position: 0 1 2 3 4 5

>>> p = ['P','y','t','h','o','n']
# Why the two sets of numbers:
# indexing gives items, not lists
>>> p[0]
'P'
>>> p[5]
'n'

# Slicing gives lists
>>> p[0:1]
['P']
>>> p[0:2]
['P','y']

Одна эвристика заключается в том, что для среза от нуля до n подумайте: "ноль - это начало, начните с начала и возьмите n элементов в списке".

>>> p[5] # the last of six items, indexed from zero
'n'
>>> p[0:5] # does NOT include the last item!
['P','y','t','h','o']
>>> p[0:6] # not p[0:5]!!!
['P','y','t','h','o','n']

Еще одна эвристика: "для любого среза замените начало на ноль, примените предыдущую эвристику, чтобы получить конец списка, затем пересчитайте первое число обратно, чтобы отсечь элементы из начала"

>>> p[0:4] # Start at the beginning and count out 4 items
['P','y','t','h']
>>> p[1:4] # Take one item off the front
['y','t','h']
>>> p[2:4] # Take two items off the front
['t','h']
# etc.

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

>>> p[2:3]
['t']
>>> p[2:3] = ['T']
>>> p
['P','y','T','h','o','n']
>>> p[2:3] = 't'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only assign an iterable

The second rule of slice assignment, which you can also see above, is that whatever portion of the list is returned by slice indexing, that's the same portion that is changed by slice assignment:

>>> p[2:4]
['T','h']
>>> p[2:4] = ['t','r']
>>> p
['P','y','t','r','o','n']

The third rule of slice assignment is, the assigned list (iterable) doesn't have to have the same length; the indexed slice is simply sliced out and replaced en masse by whatever is being assigned:

>>> p = ['P','y','t','h','o','n'] # Start over
>>> p[2:4] = ['s','p','a','m']
>>> p
['P','y','s','p','a','m','o','n']

The trickiest part to get used to is assignment to empty slices. Using heuristic 1 and 2 it's easy to get your head around indexing an empty slice:

>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
['P','y','t','h']
>>> p[1:4]
['y','t','h']
>>> p[2:4]
['t','h']
>>> p[3:4]
['h']
>>> p[4:4]
[]

And then once you've seen that, slice assignment to the empty slice makes sense too:

>>> p = ['P','y','t','h','o','n']
>>> p[2:4] = ['x','y'] # Assigned list is same length as slice
>>> p
['P','y','x','y','o','n'] # Result is same length
>>> p = ['P','y','t','h','o','n']
>>> p[3:4] = ['x','y'] # Assigned list is longer than slice
>>> p
['P','y','t','x','y','o','n'] # The result is longer
>>> p = ['P','y','t','h','o','n']
>>> p[4:4] = ['x','y']
>>> p
['P','y','t','h','x','y','o','n'] # The result is longer still

Обратите внимание, что, поскольку мы не меняем второй номер среза (4), вставленные элементы всегда складываются прямо напротив буквы "o", даже когда мы присваиваем пустой срез. Таким образом, позиция для назначения пустого среза является логическим продолжением позиций для назначений непустого среза.

Немного отступая, что происходит, когда вы продолжаете наш процесс подсчета начала среза?

>>> p = ['P','y','t','h','o','n']
>>> p[0:4]
['P','y','t','h']
>>> p[1:4]
['y','t','h']
>>> p[2:4]
['t','h']
>>> p[3:4]
['h']
>>> p[4:4]
[]
>>> p[5:4]
[]
>>> p[6:4]
[]

С нарезкой, как только вы закончите, вы закончите; она не начинает нарезку в обратном направлении. В Python вы не получите отрицательных результатов, если вы явно не запросите их, используя отрицательное число.

>>> p[5:3:-1]
['n','o']

Правило "как только вы закончите, все готово" имеет некоторые странные последствия:

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

На самом деле, по сравнению с индексацией, нарезка в Python на удивление безошибочна:

>>> p[100:200]
[]
>>> p[int(2e99):int(1e99)]
[]

Иногда это может пригодиться, но также может привести к несколько странному поведению:

>>> p
['P', 'y', 't', 'h', 'o', 'n']
>>> p[int(2e99):int(1e99)] = ['p','o','w','e','r']
>>> p
['P', 'y', 't', 'h', 'o', 'n', 'p', 'o', 'w', 'e', 'r']

В зависимости от вашего приложения это может быть ... а может и нет... это то, на что вы надеялись!


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

>>> r=[1,2,3,4]
>>> r[1:1]
[]
>>> r[1:1]=[9,8]
>>> r
[1, 9, 8, 2, 3, 4]
>>> r[1:1]=['blah']
>>> r
[1, 'blah', 9, 8, 2, 3, 4]

Это также может прояснить разницу между нарезкой и индексацией.

python