Compreender a notação da fatia

Eu preciso de uma boa explicação (as referências são uma vantagem) na notação de fatias Python's.

Para mim, esta notação precisa de um pouco de captação.

Parece extremamente poderoso, mas eu tenho'não tenho bem a minha cabeça à volta disso.

Solução

É muito simples, na verdade:

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

Há também o valor do "passo", que pode ser utilizado com qualquer uma das opções acima:

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

O ponto chave a lembrar é que o valor :stop' representa o primeiro valor que é *não* na fatia selecionada. Portanto, a diferença entrestopestarté o número de elementos selecionados (sestep` for 1, o padrão).

A outra característica é que start ou stop pode ser um número negativo, o que significa que conta a partir do fim do array ao invés do início. Então:

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

Da mesma forma, step pode ser um número negativo:

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 é gentil para o programador se houver menos itens do que você pede. Por exemplo, se você pedir a[:-2] e `a' contém apenas um elemento, você recebe uma lista vazia ao invés de um erro. Algumas vezes você prefere o erro, então você tem que estar ciente de que isso pode acontecer.

Relação com slice()objeto

O operador de fatiamento [] está sendo utilizado no código acima com um objeto slice() utilizando a notação : (que só é válida dentro de []), ou seja:

a[start:stop:step]

é equivalente a:

a[slice(start, stop, step)]

Os objetos da fatia também se comportam de forma ligeiramente diferente dependendo do número de argumentos, de forma similar a range(), ou seja, ambos slice(stop) e slice(start, stop[, step]) são suportados. Para pular a especificação de um dado argumento, pode-se utilizar None, de modo que por exemplo a[start:] seja equivalente a a[slice(start, None)] ou a[::-1] seja equivalente a a[slice(None, None, -1)].

Enquanto a notação :based notation é muito útil para fatias simples, a utilização explícita de objetos slice() simplifica a geração programática de fatias.

Comentários (7)

O Python tutorial fala sobre isso (desça um pouco até chegar à parte sobre fatiar).

O diagrama de arte ASCII também é útil para lembrar como funcionam as fatias:

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

Uma maneira de lembrar como as fatias funcionam é pensar nos índices como apontando entre caracteres, com a borda esquerda do primeiro caractere numerada 0. Depois a borda direita do último caractere de uma cadeia de caracteres n tem índice n.

Comentários (2)

Enumerando as possibilidades permitidas pela gramática:

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

Claro, se (high-low)%stride != 0, então o ponto final será um pouco mais baixo que high-1.

Se o "stride" for negativo, o pedido é alterado um pouco, já que estamos em contagem regressiva:

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

Os cortes estendidos (com vírgulas e elipses) são usados principalmente apenas por estruturas de dados especiais (como o NumPy); as sequências básicas não as suportam.

>>> class slicee:
...     def __getitem__(self, item):
...         return repr(item)
...
>>> slicee()[0, 1:2, ::5, ...]
'(0, slice(1, 2, None), slice(None, None, 5), Ellipsis)'
Comentários (3)