separar Entendendo a notação de fatia do Python




substring em python (20)

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

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

Parece extremamente poderoso, mas ainda não entendi direito.

https://code.i-harness.com


Explique a notação de fatia do Python

Em suma, os dois-pontos (:) em notação subscrita ( subscriptable[subscriptarg] ) fazem a notação de fatia - que tem os argumentos opcionais, start , stop , step :

sliceable[start:stop:step]

O fatiamento em Python é uma maneira computacionalmente rápida de acessar metodicamente partes de seus dados. Na minha opinião, para ser um programador Python intermediário, é um aspecto da linguagem que é necessário estar familiarizado.

Definições importantes

Para começar, vamos definir alguns termos:

start: o índice inicial da fatia, incluirá o elemento nesse índice, a menos que seja o mesmo que stop , o padrão é 0, ou seja, o primeiro índice. Se for negativo, significa iniciar n itens do final.

stop: o índice final da fatia, ele não inclui o elemento neste índice, o padrão é o tamanho da seqüência que está sendo fatiada, ou seja, até e incluindo o final.

step: o valor pelo qual o índice aumenta, o padrão é 1. Se for negativo, você está cortando o iterável ao contrário.

Como funciona a indexação

Você pode fazer qualquer um desses números positivos ou negativos. O significado dos números positivos é simples, mas para números negativos, assim como índices em Python, você conta de trás para frente no início e no fim , e para o passo , simplesmente decrementa seu índice. Este exemplo é do tutorial da documentação , mas eu o modifiquei ligeiramente para indicar em qual item de uma sequência cada referência de índice:

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

Como funciona o fatiamento

Para usar a notação de fatia com uma sequência que a suporte, você deve incluir pelo menos dois pontos nos colchetes que seguem a sequência (que, na verdade, implementam o método __getitem__ da sequência, de acordo com o modelo de dados Python ).

A notação de fatia funciona assim:

sequence[start:stop:step]

E lembre-se de que há padrões para iniciar , parar e passo , portanto, para acessar os padrões, simplesmente deixe de fora o argumento.

Notação de fatia para obter os últimos nove elementos de uma lista (ou qualquer outra sequência que suporte, como uma string) seria assim:

my_list[-9:]

Quando vejo isso, leio a parte entre parênteses como "9 do final até o fim". (Na verdade, abrevio mentalmente como "-9, on")

Explicação:

A notação completa é

my_list[-9:None:None]

e para substituir os padrões (na verdade, quando step é negativo, o padrão de -len(my_list) - 1 é -len(my_list) - 1 , então None para stop realmente significa que ele vai para qualquer etapa final):

my_list[-9:len(my_list):1]

Os dois pontos , : , é o que diz ao Python que você está dando uma fatia e não um índice regular. É por isso que a maneira idiomática de criar uma cópia superficial de listas no Python 2 é

list_copy = sequence[:]

E limpá-los é com:

del my_list[:]

(O Python 3 obtém um método list.copy e list.clear .)

Quando o step é negativo, os padrões para start e stop mudança

Por padrão, quando o argumento da step está vazio (ou None ), ele é atribuído a +1 .

Mas você pode passar um número inteiro negativo e a lista (ou a maioria dos slicables padrão) será dividida do final para o início.

Assim, uma fatia negativa mudará os padrões para start e stop !

Confirmando isso na fonte

Eu gosto de incentivar os usuários a lerem a fonte e também a documentação. O código fonte para objetos de fatia e essa lógica é encontrado aqui . Primeiro, determinamos se o step é negativo:

 step_is_negative = step_sign < 0;

Nesse caso, o limite inferior é -1 o que significa que dividimos todo o caminho até e incluindo o início, e o limite superior é o comprimento menos 1, o que significa que começamos no final. (Note que a semântica deste -1 é diferente de um -1 que os usuários podem passar índices no Python indicando o último item.)

if (step_is_negative) {
    lower = PyLong_FromLong(-1L);
    if (lower == NULL)
        goto error;

    upper = PyNumber_Add(length, lower);
    if (upper == NULL)
        goto error;
}

Caso contrário, o step é positivo, e o limite inferior será zero e o limite superior (que vamos até, mas não inclui), o comprimento da lista fatiada.

else {
    lower = _PyLong_Zero;
    Py_INCREF(lower);
    upper = length;
    Py_INCREF(upper);
}

Então, podemos precisar aplicar os padrões para start e stop - o padrão para o start é calculado como o limite superior quando a step é negativa:

if (self->start == Py_None) {
    start = step_is_negative ? upper : lower;
    Py_INCREF(start);
}

e stop , o limite inferior:

if (self->stop == Py_None) {
    stop = step_is_negative ? lower : upper;
    Py_INCREF(stop);
}

Dê às suas fatias um nome descritivo!

Você pode achar útil separar a formação da fatia passando-a para o método list.__getitem__ ( isso é o que os colchetes fazem ). Mesmo que você não seja novo, ele mantém seu código mais legível para que outros que tenham que ler seu código entendam mais facilmente o que você está fazendo.

No entanto, você não pode simplesmente atribuir alguns inteiros separados por dois pontos a uma variável. Você precisa usar o objeto de fatia:

last_nine_slice = slice(-9, None)

O segundo argumento, None , é necessário, de modo que o primeiro argumento seja interpretado como o argumento de start caso contrário, seria o argumento de stop .

Você pode então passar o objeto fatia para sua sequência:

>>> list(range(100))[last_nine_slice]
[91, 92, 93, 94, 95, 96, 97, 98, 99]

É interessante que os intervalos também tenham fatias:

>>> range(100)[last_nine_slice]
range(91, 100)

Considerações de memória:

Uma vez que fatias de listas do Python criam novos objetos na memória, outra função importante a ter em conta é itertools.islice . Normalmente, você desejará iterar sobre uma fatia e não apenas criá-la estaticamente na memória. islice é perfeito para isso. Uma ressalva, ele não suporta argumentos negativos para start , stop ou step , então, se isso é um problema, talvez seja necessário calcular índices ou reverter o iterável com antecedência.

length = 100
last_nine_iter = itertools.islice(list(range(length)), length-9, None, 1)
list_last_nine = list(last_nine_iter)

e agora:

>>> list_last_nine
[91, 92, 93, 94, 95, 96, 97, 98, 99]

O fato de que as listas de listas fazem uma cópia é um recurso das próprias listas. Se você está cortando objetos avançados como um Pandas DataFrame, ele pode retornar uma visão do original, e não uma cópia.


1. notação da fatia

Para simplificar, lembre-se que o slice tem apenas um formulário:

s[start:end:step]

e aqui está como funciona:

  • s : um objeto que pode ser cortado
  • start : primeiro índice para iniciar a iteração
  • end : last index, NOTE que o índice end não será incluído na fatia resultante
  • step : escolha elemento a cada step

Outra coisa importante: todo start , end , step pode ser omitido! E se eles forem omitidos, o valor padrão será usado: 0 , len(s) , 1 acordo.

Então possíveis variações são:

# mostly used variations
s[start:end]
s[start:]
s[:end]

# step related variations
s[:end:step]
s[start::step]
s[::step]

# make a copy
s[:]

NOTA: Se start>=end (considerando apenas quando o step>0 ), python retornará uma fatia vazia [] .

2. Armadilhas

A parte acima explica os principais recursos de como o slice funciona, ele funcionará na maioria das ocasiões. No entanto, pode haver armadilhas que você deve tomar cuidado, e essa parte as explica.

Índices negativos

A primeira coisa que confunde os aprendizes de Python é que o índice pode ser negativo! Não entre em pânico: o índice negativo significa contar de trás para frente.

Por exemplo:

s[-5:]    # start at the 5th index from the end of array, 
          # thus returns the last 5 elements
s[:-5]    # start at index 0, end until the 5th index from end of array, 
          # thus returns s[0:len(s)-5]

Passo negativo

Tornar as coisas mais confusas também steppode ser negativo!

Etapa negativa significa iterar a matriz para trás: de ponta a ponta, com o índice final incluído e o índice inicial excluído do resultado.

NOTA : quando passo é negativo, o valor padrão para starta len(s)(enquanto endnão é igual a 0, porque s[::-1]contém s[0]). Por exemplo:

s[::-1]            # reversed slice
s[len(s)::-1]      # same as above, reversed slice
s[0:len(s):-1]     # empty list

Erro fora do intervalo?

Surpreenda-se: o slice não aumenta IndexError quando o índice está fora do intervalo!

Se o índice estiver fora do intervalo, o python tentará configurar o índice para 0ou de len(s)acordo com a situação. Por exemplo:

s[:len(s)+5]      # same as s[:len(s)]
s[-len(s)-5::]    # same as s[0:]
s[len(s)+5::-1]   # same as s[len(s)::-1], same as s[::-1]

3. Exemplos

Vamos terminar esta resposta com exemplos explica tudo o que discutimos:

# create our array for demonstration
In [1]: s = [i for i in range(10)]

In [2]: s
Out[2]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [3]: s[2:]   # from index 2 to last index
Out[3]: [2, 3, 4, 5, 6, 7, 8, 9]

In [4]: s[:8]   # from index 0 up to index 8
Out[4]: [0, 1, 2, 3, 4, 5, 6, 7]

In [5]: s[4:7]  # from index 4(included) up to index 7(excluded)
Out[5]: [4, 5, 6]

In [6]: s[:-2]  # up to second last index(negative index)
Out[6]: [0, 1, 2, 3, 4, 5, 6, 7]

In [7]: s[-2:]  # from second last index(negative index)
Out[7]: [8, 9]

In [8]: s[::-1] # from last to first in reverse order(negative step)
Out[8]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

In [9]: s[::-2] # all odd numbers in reversed order
Out[9]: [9, 7, 5, 3, 1]

In [11]: s[-2::-2] # all even numbers in reversed order
Out[11]: [8, 6, 4, 2, 0]

In [12]: s[3:15]   # end is out of range, python will set it to len(s)
Out[12]: [3, 4, 5, 6, 7, 8, 9]

In [14]: s[5:1]    # start > end, return empty list
Out[14]: []

In [15]: s[11]     # access index 11(greater than len(s)) will raise IndexError
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-15-79ffc22473a3> in <module>()
----> 1 s[11]

IndexError: list index out of range

As respostas acima não discutem a atribuição de fatia. Para entender a atribuição de fatia, é útil adicionar outro conceito à arte 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']

Uma heurística é, para uma fatia de zero a n, pensar: "zero é o começo, começa no começo e pega n itens em uma lista".

>>> 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']

Outra heurística é: "para qualquer fatia, substitua o início por zero, aplique a heurística anterior para obter o final da lista e, em seguida, conte o primeiro número de volta para cortar os itens no início"

>>> 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.

A primeira regra de atribuição de fatia é que, como o fatiamento retorna uma lista, a atribuição de fatia requer uma lista (ou outra iterável):

>>> 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

A segunda regra de atribuição de fatia, que você também pode ver acima, é que qualquer parte da lista é retornada pela indexação de fatia, essa é a mesma parte que é alterada pela atribuição de fatia:

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

A terceira regra da atribuição de fatia é que a lista atribuída (iterável) não precisa ter o mesmo comprimento; a fatia indexada é simplesmente cortada e substituída em massa pelo que está sendo atribuído:

>>> 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']

A parte mais complicada para se acostumar é a atribuição de fatias vazias. Usando a heurística 1 e 2 é fácil entender a indexação de uma fatia vazia:

>>> 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]
 []

E depois de ver isso, a atribuição de fatia à fatia vazia também faz sentido:

>>> 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'] # result is longer
>>> p = ['P','y','t','h','o','n']
>>> p[4:4] = ['x','y']
>>> p
 ['P','y','t','h','x','y','o','n'] # result is longer still

Note que, como não estamos alterando o segundo número da fatia (4), os itens inseridos sempre são empilhados contra o 'o', mesmo quando estamos atribuindo a fatia vazia. Portanto, a posição para a atribuição de fatia vazia é a extensão lógica das posições para as atribuições de fatia não vazias.

Fazendo o backup um pouco, o que acontece quando você continua com a nossa procissão de contar a fatia começando?

>>> 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]
 []

Com o fatiamento, quando terminar, estará pronto; não começa a cortar para trás. No Python, você não recebe passos negativos, a menos que você peça explicitamente por eles usando um número negativo.

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

Há algumas conseqüências estranhas para a regra "uma vez terminado, pronto":

>>> 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

De fato, comparado à indexação, o fatiamento de Python é bizarramente à prova de erros:

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

Isso pode ser útil às vezes, mas também pode levar a um comportamento um tanto estranho:

>>> 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']

Dependendo da sua aplicação, isso pode ... ou não ... ser o que você esperava lá!

Abaixo está o texto da minha resposta original, tem sido útil para muitas pessoas, então eu não queria apagá-lo.

>>> r=[0,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]

Isso também pode esclarecer a diferença entre fatiamento e indexação.


Como regra geral, escrever código com muitos valores de índice codificados leva a uma facilidade de leitura e manutenção. Por exemplo, se você voltar ao código um ano depois, você verá o que estava pensando ao escrevê-lo. A solução mostrada é simplesmente uma forma de indicar mais claramente o que seu código está realmente fazendo. Em geral, a fatia interna () cria um objeto de fatia que pode ser usado em qualquer lugar que uma fatia é permitida. Por exemplo:

>>> items = [0, 1, 2, 3, 4, 5, 6]
>>> a = slice(2, 4)
>>> items[2:4]
[2, 3]
>>> items[a]
[2, 3]
>>> items[a] = [10,11]
>>> items
[0, 1, 10, 11, 4, 5, 6]
>>> del items[a]
>>> items
[0, 1, 4, 5, 6]

Se você tiver uma instância de fatia s, poderá obter mais informações sobre ela observando seus atributos s.start, s.stop e s.step, respectivamente. Por exemplo:

>>> a = slice(10, 50, 2)
>>> a.start
10
>>> a.stop
50
>>> a.step
2
>>>

E algumas coisas que não eram imediatamente óbvias para mim quando vi pela primeira vez a sintaxe do fatiamento:

>>> x = [1,2,3,4,5,6]
>>> x[::-1]
[6,5,4,3,2,1]

Maneira fácil de reverter seqüências!

E se você quisesse, por algum motivo, cada segundo item na seqüência invertida:

>>> x = [1,2,3,4,5,6]
>>> x[::-2]
[6,4,2]

Encontrei esta excelente tabela em http://wiki.python.org/moin/MovingToPythonFromOtherLanguages

>>> l1
[2, 3, 4]

>>> l1[:]
[2, 3, 4]

>>> l1[::-1] # a default is -1 , b default is -(len+1)
[4, 3, 2]

>>> l1[:-4:-1] # a default is -1
[4, 3, 2]

>>> l1[:-3:-1] # a default is -1
[4, 3]

>>> l1[::] # c default is +1, so a default is 0, b default is len
[2, 3, 4]

>>> l1[::-1] # c is -1 , so a default is -1 and b default is -(len+1)
[4, 3, 2]


>>> l1[-100:-200:-1] # Interesting
[]

>>> l1[-1:-200:-1] # Interesting
[4, 3, 2]


>>> l1[-1:-1:1]
[]


>>> l1[-1:5:1] # Interesting
[4]


>>> l1[1:-7:1]
[]

>>> l1[1:-7:-1] # Interesting
[3, 2]

>>> l1[:-2:-2] # a default is -1, stop(b) at -2 , step(c) by 2 in reverse direction
[4]

Eu acho mais fácil lembrar como funciona, então eu posso descobrir qualquer combinação específica de iniciar / parar / passo.

É instrutivo entender o range() primeiro:

def range(start=0, stop, step=1):  # illegal syntax, but that's the effect
    i = start
    while (i < stop if step > 0 else i > stop):
        yield i
        i += step

Comece do start , incremente step , não alcance a stop . Muito simples.

A coisa a lembrar sobre o passo negativo é que a stop é sempre o fim excluído, seja mais alto ou mais baixo. Se você quiser a mesma fatia na ordem oposta, é muito mais limpo fazer a reversão separadamente: por exemplo, 'abcde'[1:-2][::-1] corta um char da esquerda, dois da direita e depois inverte. (Veja também reversed() .)

O fatiamento de sequência é o mesmo, exceto que primeiro normaliza os índices negativos e nunca pode sair da sequência:

TODO : O código abaixo tinha um bug com "nunca sair da sequência" quando abs (passo)> 1; Acho que corrigi tudo para estar correto, mas é difícil de entender.

def this_is_how_slicing_works(seq, start=None, stop=None, step=1):
    if start is None:
        start = (0 if step > 0 else len(seq)-1)
    elif start < 0:
        start += len(seq)
    if not 0 <= start < len(seq):  # clip if still outside bounds
        start = (0 if step > 0 else len(seq)-1)
    if stop is None:
        stop = (len(seq) if step > 0 else -1)  # really -1, not last element
    elif stop < 0:
        stop += len(seq)
    for i in range(start, stop, step):
        if 0 <= i < len(seq):
            yield seq[i]

Não se preocupe com os Detalhes do is None - lembre-se que omitir start e / ou stop sempre faz a coisa certa para lhe dar toda a seqüência.

A normalização dos índices negativos primeiro permite que o início e / ou a parada sejam contados a partir do final independentemente: 'abcde'[1:-2] == 'abcde'[1:3] == 'bc' apesar do range(1,-2) == [] A normalização é algumas vezes considerada como "modulo o comprimento" mas note que adiciona o comprimento apenas uma vez: eg 'abcde'[-53:42] é apenas a string inteira.


Eu uso o método "um ponto de índice entre os elementos" de pensar sobre ele mesmo, mas uma maneira de descrevê-lo, que às vezes ajuda os outros a entender, é esta:

mylist[X:Y]

X é o índice do primeiro elemento que você deseja.
Y é o índice do primeiro elemento que você não deseja.


Isto é como eu ensino fatias para iniciantes:

Entendendo a diferença entre indexação e fatiamento:

O Wiki Python tem essa incrível imagem que distingue claramente a indexação e o fatiamento.

É uma lista com 6 elementos. Para entender melhor o fatiamento, considere essa lista como um conjunto de seis caixas colocadas juntas. Cada caixa tem um alfabeto.

A indexação é como lidar com o conteúdo da caixa. Você pode verificar o conteúdo de qualquer caixa. Mas você não pode verificar o conteúdo de várias caixas de uma só vez. Você pode até mesmo substituir o conteúdo da caixa. Mas você não pode colocar 2 bolas em 1 caixa ou substituir 2 bolas de cada vez.

>>> l=[12,23,345,456,67,7,945,467]

Slicing é como lidar com caixas em si. Você pode pegar a primeira caixa e colocá-la em outra mesa. Para pegar a caixa tudo que você precisa saber é a posição de início e fim da caixa.

Você pode até pegar as 3 primeiras caixas ou as últimas 2 caixas ou todas as caixas entre 1 e 4. Então, você pode escolher qualquer conjunto de caixas se você souber o começo e o fim. Essas posições são chamadas de posições de início e parada.

O interessante é que você pode substituir várias caixas de uma só vez. Você também pode colocar várias caixas onde você quiser.

>>> l[len(l):-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[len(l)::-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[::-1]
[467, 945, 7, 67, 456, 345, 23, 12]

>>> l[-1:-len(l)-1:-1]
[467, 945, 7, 67, 456, 345, 23, 12]

Fatiar com o passo:

Até agora você escolheu caixas continuamente. Mas algumas vezes você precisa pegar discretamente. Por exemplo, você pode pegar cada segunda caixa. Você pode até pegar cada terceira caixa a partir do final. Esse valor é chamado de tamanho da etapa. Isso representa a lacuna entre suas captações sucessivas. O tamanho do passo deve ser positivo se você estiver escolhendo caixas do começo ao fim e vice-versa.

In [122]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']

In [123]: alpha
Out[123]: ['a', 'b', 'c', 'd', 'e', 'f']

In [124]: alpha[0]
Out[124]: 'a'

In [127]: alpha[0] = 'A'

In [128]: alpha
Out[128]: ['A', 'b', 'c', 'd', 'e', 'f']

In [129]: alpha[0,1]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-129-c7eb16585371> in <module>()
----> 1 alpha[0,1]

TypeError: list indices must be integers, not tuple

Como o Python descobre os parâmetros ausentes:

Ao fatiar se você deixar de fora qualquer parâmetro, o Python tentará descobrir isso automaticamente.

Se você verificar o código-fonte do CPython, você encontrará uma função chamada PySlice_GetIndicesEx, que descobre os índices para uma fatia para qualquer parâmetro dado. Aqui está o código equivalente lógico em Python.

Essa função usa um objeto Python e parâmetros opcionais para fatiar e retorna start, stop, step e slice length para a fatia solicitada.

In [130]: alpha[0:1]
Out[130]: ['A']

In [131]: alpha[0:1] = 'a'

In [132]: alpha
Out[132]: ['a', 'b', 'c', 'd', 'e', 'f']

In [133]: alpha[0:2] = ['A', 'B']

In [134]: alpha
Out[134]: ['A', 'B', 'c', 'd', 'e', 'f']

In [135]: alpha[2:2] = ['x', 'xx']

In [136]: alpha
Out[136]: ['A', 'B', 'x', 'xx', 'c', 'd', 'e', 'f']

Essa é a inteligência que está presente por trás das fatias. Como o Python tem uma função embutida chamada slice, você pode passar alguns parâmetros e verificar com que inteligência calcula parâmetros ausentes.

In [137]: alpha = ['a', 'b', 'c', 'd', 'e', 'f']

In [142]: alpha[1:5:2] 
Out[142]: ['b', 'd']

In [143]: alpha[-1:-5:-2]
Out[143]: ['f', 'd']

In [144]: alpha[1:5:-2]
Out[144]: []

In [145]: alpha[-1:-5:2]      
Out[145]: []

Nota: Este post foi originalmente escrito em meu blog http://www.avilpage.com/2015/03/a-slice-of-python-intelligence-behind.html


No Python 2.7

Fatiar em Python

Python indexes and slices for a six-element list.
Indexes enumerate the elements, slices enumerate the spaces between the elements.

Index from rear:    -6  -5  -4  -3  -2  -1      a=[0,1,2,3,4,5]    a[1:]==[1,2,3,4,5]
Index from front:    0   1   2   3   4   5      len(a)==6          a[:5]==[0,1,2,3,4]
                   +---+---+---+---+---+---+    a[0]==0            a[:-2]==[0,1,2,3]
                   | a | b | c | d | e | f |    a[5]==5            a[1:2]==[1]
                   +---+---+---+---+---+---+    a[-1]==5           a[1:-1]==[1,2,3,4]
Slice from front:  :   1   2   3   4   5   :    a[-2]==4
Slice from rear:   :  -5  -4  -3  -2  -1   :
                                                b=a[:]
                                                b==[0,1,2,3,4,5] (shallow copy of a)

Compreender a atribuição de índice é muito importante.

[a:b:c]

len = length of string, tuple or list

c -- default is +1. The sign of c indicates forward or backward, absolute value of c indicates steps. Default is forward with step size 1. Positive means forward, negative means backward.

a --  When c is positive or blank, default is 0. When c is negative, default is -1.

b --  When c is positive or blank, default is len. When c is negative, default is -(len+1).

Quando você diz [a: b: c], você está dizendo, dependendo do sinal de c (para frente ou para trás), comece em a e termine em b (excluindo o elemento no índice bth). Use a regra de indexação acima e lembre-se de que você encontrará apenas elementos nesse intervalo:

In forward direction, starts at 0 and ends at len-1

In backward direction, starts at -1 and ends at -len

Mas esse alcance continua infinitamente em ambas as direções:

-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1

Por exemplo:

...,-len -2 ,-len-1,-len, -len+1, -len+2, ..., 0, 1, 2,3,4 , len -1, len, len +1, len+2 , ....

Se a sua escolha de a, b e c permitir a sobreposição com o intervalo acima à medida que você percorre as regras para a, b, c acima, você obterá uma lista com elementos (tocados durante a travessia) ou obterá uma lista vazia.

Uma última coisa: se aeb são iguais, então você também recebe uma lista vazia:

             0    1    2   3    4   5   6   7   8   9   10   11
             a    s    t   r    i   n   g
    -9  -8  -7   -6   -5  -4   -3  -2  -1

O tutorial do Python fala sobre isso (role um pouco até chegar na parte sobre fatiar).

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

 +---+---+---+---+---+---+
 | 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 os caracteres, com a borda esquerda do primeiro caractere numerada como 0. Em seguida, a borda direita do último caractere de uma sequência de n caracteres possui o índice n .


Você também pode usar a atribuição de fatia para remover um ou mais elementos de uma lista:

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

A técnica básica de fatiamento é definir o ponto de partida, o ponto de parada e o tamanho do passo - também conhecido como passo.

Primeiro, vamos criar uma lista de valores para usar em nosso fatiamento.

Crie duas listas para dividir, a primeira é uma lista numérica de 1 a 9 (Lista A). O segundo também é uma lista numérica, de 0 a 9 (Lista B)

A = list(range(1,10,1)) # start,stop,step
B = list(range(9))

print("This is List A:",A)
print("This is List B:",B)

Indexar o número 3 de A e o número 6 de B.

print(A[2])
print(B[6])

Fatias Básicas

A sintaxe de indexação estendida usada para fatiar é aList [start: stop: step]. O argumento start e o argumento step são padronizados para none - o único argumento obrigatório é stop. Você notou que isso é semelhante a como o intervalo foi usado para definir as listas A e B? Isso ocorre porque o objeto de fatia representa o conjunto de índices especificado por intervalo (iniciar, parar, etapa). Documentação do Python 3.4

Como você pode ver, definir apenas stop retorna um elemento. Como o padrão inicial é nenhum, isso se traduz em recuperar apenas um elemento.

É importante notar que o primeiro elemento é o índice 0, NÃO o índice 1. É por isso que estamos usando 2 listas para este exercício. Os elementos da Lista A são numerados de acordo com a posição ordinal (o primeiro elemento é 1, o segundo elemento é 2, etc) enquanto os elementos da Lista B são os números que seriam usados ​​para indexá-los ([0] para o primeiro elemento 0, etc ).

Com a sintaxe de indexação estendida, recuperamos um intervalo de valores. Por exemplo, todos os valores são recuperados com dois pontos.

A[:]

Para recuperar um subconjunto de elementos, as posições de início e parada precisam ser definidas.

Dado o padrão aList [start: stop], recupere os dois primeiros elementos da Listagem A


As respostas acima não discutem o fatiamento da matriz multi-dimensional que é possível usando o pacote numpy famoso:

O fatiamento também se aplica a matrizes multidimensionais.

# Here, a is a numpy array

>>> a
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12]])
>>> a[:2,0:3:2]
array([[1, 3],
       [5, 7]])

O ": 2" antes da vírgula opera na primeira dimensão e o "0: 3: 2" após a vírgula operar na segunda dimensão.


Hehehe, é meio estranho me ver tentando fornecer uma explicação melhor e mais simples depois de mais de 2600 votos sobre o que foi marcado como a resposta certa de Grew Hewgill.

Aqui vamos nós ...

Na minha opinião, você entenderá e memorizará melhor a notação de fatiamento de strings do Python se você olhar para ela da seguinte maneira (continue lendo).

Vamos trabalhar com a seguinte string ...

azString = "abcdefghijklmnopqrstuvwxyz"

Para quem não sabe, você pode criar qualquer substring azStringusando a notaçãoazString[x:y]

Vindo de outras linguagens de programação, é quando o senso comum fica comprometido. Quais são xe y?

Eu tive que sentar e executar vários cenários em minha busca por uma técnica de memorização que me ajudaria a lembrar o que xey são e me ajudar a cortar as cordas corretamente na primeira tentativa.

Minha conclusão é que x e y devem ser vistos como os índices de limite que cercam as strings que queremos extra. Então devemos ver a expressão como azString[index1, index2]ou até mais clara como azString[index_of_first_character, index_after_the_last_character].

Aqui está um exemplo de visualização disso ...

Letters abcdefghij ... ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ Indexes 0 1 2 3 4 5 6 7 8 9 ... | | cdefgh index1 index2

Portanto, tudo o que você precisa fazer é definir index1 e index2 com os valores que circundarão a substring desejada. Por exemplo, para obter a substring "cdefgh", você pode usar azString[2:8]porque o índice do lado esquerdo de "c" é 2 e o do tamanho certo de "h" é 8.

Lembre-se, estamos estabelecendo os limites.

Esse truque funciona o tempo todo e é fácil de memorizar.

Hopefuly isso ajudará.


O abaixo é o exemplo do índice de uma string

 +---+---+---+---+---+
 | H | e | l | p | A |
 +---+---+---+---+---+
 0   1   2   3   4   5
-5  -4  -3  -2  -1

str="Name string"

Exemplo de fatiamento: [start: end: step]

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

Abaixo está o exemplo de uso

print str[0]=N
print str[0:2]=Na
print str[0:7]=Name st
print str[0:7:2]=Nm t
print str[0:-1:2]=Nm ti

A maioria das respostas acima esclarece sobre a notação de Slice. Sintaxe de indexação estendida usada para fatiar é aList[start:stop:step]exemplos básicos são

:

Mais exemplos de fatiamento: 15 fatias estendidas


Eu quero adicionar um exemplo Hello world que explique os fundamentos das fatias para os iniciantes. Isto me ajudou bastante.

Vamos ter uma lista com seis valores ['P', 'Y', 'T', 'H', 'O', 'N']:

+---+---+---+---+---+---+
| P | Y | T | H | O | N |
+---+---+---+---+---+---+
  0   1   2   3   4   5 

Agora, as fatias mais simples dessa lista são suas sub-listas. A notação é [<index>:<index>]e a chave é ler assim:

[ start cutting before this index : end cutting before this index ]

Agora, se você fizer uma parte [2:5]da lista acima, isso acontecerá:

        |           |
+---+---|---+---+---|---+
| P | Y | T | H | O | N |
+---+---|---+---+---|---+
  0   1 | 2   3   4 | 5 

Você fez um corte antes do elemento com índice 2e outro corte antes do elemento com índice 5. Então o resultado será uma fatia entre esses dois cortes, uma lista ['T', 'H', 'O'].


Se você acha que os índices negativos no fatiamento são confusos, aqui está uma maneira muito fácil de pensar a respeito: basta substituir o índice negativo por len - index. Por exemplo, substitua -3 por len(list) - 3.

A melhor maneira de ilustrar o que o fatiamento faz internamente é apenas mostrá-lo no código que implementa essa operação:

def slice(list, start = None, end = None, step = 1):
  # take care of missing start/end parameters
  start = 0 if start is None else start
  end = len(list) if end is None else end

  # take care of negative start/end parameters
  start = len(list) + start if start < 0 else start
  end = len(list) + end if end < 0 else end

  # now just execute for-loop with start, end and step
  return [list[i] for i in range(start, end, step)]

#!/usr/bin/env python

def slicegraphical(s, lista):

    if len(s) > 9:
        print """Enter a string of maximum 9 characters,
    so the printig would looki nice"""
        return 0;
    # print " ",
    print '  '+'+---' * len(s) +'+'
    print ' ',
    for letter in s:
        print '| {}'.format(letter),
    print '|'
    print " ",; print '+---' * len(s) +'+'

    print " ",
    for letter in range(len(s) +1):
        print '{}  '.format(letter),
    print ""
    for letter in range(-1*(len(s)), 0):
        print ' {}'.format(letter),
    print ''
    print ''


    for triada in lista:
        if len(triada) == 3:
            if triada[0]==None and triada[1] == None and triada[2] == None:
                # 000
                print s+'[   :   :   ]' +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] == None and triada[2] != None:
                # 001
                print s+'[   :   :{0:2d} ]'.format(triada[2], '','') +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] != None and triada[2] == None:
                # 010
                print s+'[   :{0:2d} :   ]'.format(triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] == None and triada[1] != None and triada[2] != None:
                # 011
                print s+'[   :{0:2d} :{1:2d} ]'.format(triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] == None and triada[2] == None:
                # 100
                print s+'[{0:2d} :   :   ]'.format(triada[0]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] == None and triada[2] != None:
                # 101
                print s+'[{0:2d} :   :{1:2d} ]'.format(triada[0], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] != None and triada[2] == None:
                # 110
                print s+'[{0:2d} :{1:2d} :   ]'.format(triada[0], triada[1]) +' = ', s[triada[0]:triada[1]:triada[2]]
            elif triada[0] != None and triada[1] != None and triada[2] != None:
                # 111
                print s+'[{0:2d} :{1:2d} :{2:2d} ]'.format(triada[0], triada[1], triada[2]) +' = ', s[triada[0]:triada[1]:triada[2]]

        elif len(triada) == 2:
            if triada[0] == None and triada[1] == None:
                # 00
                print s+'[   :   ]    ' + ' = ', s[triada[0]:triada[1]]
            elif triada[0] == None and triada[1] != None:
                # 01
                print s+'[   :{0:2d} ]    '.format(triada[1]) + ' = ', s[triada[0]:triada[1]]
            elif triada[0] != None and triada[1] == None:
                # 10
                print s+'[{0:2d} :   ]    '.format(triada[0]) + ' = ', s[triada[0]:triada[1]]
            elif triada[0] != None and triada[1] != None:
                # 11
                print s+'[{0:2d} :{1:2d} ]    '.format(triada[0],triada[1]) + ' = ', s[triada[0]:triada[1]]

        elif len(triada) == 1:
            print s+'[{0:2d} ]        '.format(triada[0]) + ' = ', s[triada[0]]


if __name__ == '__main__':
    # Change "s" to what ever string you like, make it 9 characters for
    # better representation.
    s = 'COMPUTERS'

    # add to this list different lists to experement with indexes
    # to represent ex. s[::], use s[None, None,None], otherwise you get an error
    # for s[2:] use s[2:None]

    lista = [[4,7],[2,5,2],[-5,1,-1],[4],[-4,-6,-1], [2,-3,1],[2,-3,-1], [None,None,-1],[-5,None],[-5,0,-1],[-5,None,-1],[-1,1,-2]]

    slicegraphical(s, lista)

Você pode executar este script e experimentá-lo, abaixo estão alguns exemplos que recebi do script.

  +---+---+---+---+---+---+---+---+---+
  | C | O | M | P | U | T | E | R | S |
  +---+---+---+---+---+---+---+---+---+
  0   1   2   3   4   5   6   7   8   9   
 -9  -8  -7  -6  -5  -4  -3  -2  -1 

COMPUTERS[ 4 : 7 ]     =  UTE
COMPUTERS[ 2 : 5 : 2 ] =  MU
COMPUTERS[-5 : 1 :-1 ] =  UPM
COMPUTERS[ 4 ]         =  U
COMPUTERS[-4 :-6 :-1 ] =  TU
COMPUTERS[ 2 :-3 : 1 ] =  MPUT
COMPUTERS[ 2 :-3 :-1 ] =  
COMPUTERS[   :   :-1 ] =  SRETUPMOC
COMPUTERS[-5 :   ]     =  UTERS
COMPUTERS[-5 : 0 :-1 ] =  UPMO
COMPUTERS[-5 :   :-1 ] =  UPMOC
COMPUTERS[-1 : 1 :-2 ] =  SEUM
[Finished in 0.9s]

Ao usar um passo negativo, observe que a resposta é deslocada para a direita em 1.







slice