Entendendo Slices (Fatias) em Python

Introdução

Um slice é uma notação que retorna um pedaço/fatia de uma string, lista ou tupla em Python. Em geral, a notação não é muito difícil de ser entendida por envolver dois intervalos e um salto, porém uma certa dificuldade surge quando o salto e os intervalos assumem valores positivos e negativos.

Neste post iremos entender como os cálculos dos intervalos são feitos e quais variações possíveis desses intervalos existem para se retornar um mesmo slice.

Motivação

Eu não teria escrito esse post se não fosse um código confuso que li e que usava intervalos negativos, como em:

>>> A=range(10)
>>> A
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> A[-2:4:-1]
[8, 7, 6, 5]

No primeiro momento, tentei expandir os intervalos e ver qual seria a lógica utilizada: do -2 devo chegar ao 4 dando passos de -1. Por um momento achei que isso seria impossível mas, como podemos ver, uma lista não vazia foi retornada.

Em seguida, fiz alguns testes e verifiquei que as coisas eram mais difíceis do que eu pensava. Cheguei na conclusão de que A[-2:4:1] é igual a A[8:-6:-1] que é igual A[-2:-6:-1] e também igual a A[8:4:-1], mas não sabia explicar o motivo. Esse post tem como propósito explicar o porquê disso.

Como slices são expandidos

Um slice nada mais é que um conjunto de elementos que foram indexados de acordo com o intervalo passado. No Python, a indexação de um elemento pode ser feita tanto por índices positivos quanto negativos. O esquema é o seguinte:

  0  1  2  3  4  5  6  7  8  9
  |  |  |  |  |  |  |  |  |  |
  +++++++++++++++++++++++++++++++
  | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|
  +++++++++++++++++++++++++++++++ 
  |  |  |  |  |  |  |  |  |  | 
-10 -9 -8 -7 -6 -5 -4 -3 -2 -1

esquema de indexação.

Assim, o elemento 9 pode ser indexado tanto por -1 quanto por 9. Veja que os índices sempre indexam o elemento à direita, como em: A[2] = A[-8] = 2 e A[-4] = A[6] = 6.

Bem, um slice, como disse, é um conjunto de elementos obtidos por indexações sucessivas. Como uma indexação pode ser feita tanto por índices positivos e negativos, um mesmo slice pode ser obtido de maneiras diferentes:

A=range(10)
>>> A
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> A[2:6]
[2, 3, 4, 5]
>>> A[-8:6]
[2, 3, 4, 5]
>>> A[2:-4]
[2, 3, 4, 5]
>>> A[-8:-4]
[2, 3, 4, 5]

Os mesmos slices foram obtidos porque -8 e 2, -4 e 6 mapeiam o mesmo elemento de acordo com o esquema de indexação.

Convertendo índices

Ainda seguindo o exemplo, vemos que é bem mais simples fazermos uma leitura de A[2:6] do que de A[2:-4], por exemplo. O entendimento complica mais um pouco quando acrescentamos o fator de salto:

>>> A[-3:2:-1]
[7, 6, 5, 4, 3]
>>> A[-3:-8:-1]
[7, 6, 5, 4, 3]
>>> A[7:2:-1]
[7, 6, 5, 4, 3]

Das três formas apresentadas, a última forma, A[7:2:-1], é bem mais agradável de se ler, pois basta olharmos para a sintaxe para sabermos que todos os elementos de A[7] até, mas não incluindo, A[2] serão indexados.

O interessante é que há um modo de transformamos índices negativos em índices positivos para facilitar a leitura do slice. A lógica é trocar o índice negativo pelo seu equivalente positivo, apenas o somando com o tamanho da lista. Assim:

A[-3:2:-1]  = A[-3+10:2:-1]     = A[7:2:-1]
A[7:-2:-1]  = A[7:-2+10:-1]     = A[7:2:-1]
A[-3:-8:-1] = A[-3+10:-8+10:-1] = A[7:2:-1]

Agora, toda vez que você ver um slice com intervalo negativo por aí, você já sabe transformá-lo em um intervalo mais amigável. :D

Onde isso será útil?

Ao meu ver, isso será útil de duas maneiras: toda vez que você ver um slice com índices negativos e positivos misturados, você já sabe transformá-los para uma forma mais amigável. Isso será útil também quando você for implementar essa ideia de slice do python em algum outro lugar. Não me pergunte quando você fará isso, mas essa dúvida já rolou no stackoverflow [1]. Nesse caso, basta você receber os índices e calcular o tamanho da lista, em seguida, some esse tamanho aos índices negativos e você terá em mãos somente valores positivos e o valor de salto. Daí um loopzinho básico resolve o problema.

Referências

[1] The Python Slice Notation by stackoverflow (Acessado em: Julho/2013)
http://stackoverflow.com/questions/509211/the-python-slice-notation?rq=1

Deixe um comentário