Como a atribuição funciona com a fatia da lista Python?

100

Python doc diz que fatiar uma lista retorna uma nova lista.
Agora, se uma "nova" lista está sendo retornada, eu tenho as seguintes perguntas relacionadas a "Atribuição de fatias"

a = [1, 2, 3]
a[0:2] = [4, 5]
print a

Agora a saída seria:

[4, 5, 3] 
  1. Como algo que está retornando algo pode aparecer no lado esquerdo da expressão?
  2. Sim, li a documentação e diz que é possível, agora, uma vez que o fatiamento de uma lista retorna uma "nova" lista, por que a lista original está sendo modificada? Não consigo entender a mecânica por trás disso.
Kartik Anand
fonte
@Mark Longair desculpe, pensei que apenas o código deveria ser formatado, não a saída
Kartik Anand
7
Você entende o a[0] = 4que faria?
Josh Lee
1
A atribuição de @KartikAnand Slice é um cenário especial onde uma nova lista não é criada. Não faz sentido criar um objeto sem um vínculo de nome no lado esquerdo de um =, então, em vez de descartar isso como sintaxe inválida, o python o transforma em algo mais parecido com o que você espera. Como o python não tem referências, não funcionaria se o resultado de uma fatia alterasse a lista original. Você consegue uma cópia. Se você forneceu mais informações sobre seu aplicativo, podemos ajudá-lo melhor a fazer as coisas da maneira 'pítônica'. :)
Casey Kuball
1
@Darthfett Não estou trabalhando em nenhum aplicativo agora, em vez disso estou aprendendo python antes de começar a sujar as mãos :)
Kartik Anand

Respostas:

114

Você está confundindo duas operações distintas que usam sintaxe muito semelhante:

1) fatiar:

b = a[0:2]

Isso faz uma cópia da fatia de ae a atribui a b.

2) atribuição de fatias:

a[0:2] = b

Isso substitui a fatia de apelo conteúdo de b.

Embora a sintaxe seja semelhante (imagino pelo design!), Essas são duas operações diferentes.

NPE
fonte
4
Essa é a minha dúvida, no segundo caso, por que o slice de a não é uma lista nova ??
Kartik Anand
11
@KartikAnand Porque não é. Não é isso que a linguagem especifica.
Marcin
Para ser claro, "tira uma fatia de" realmente significa "fazer uma cópia de uma fatia de", que é de onde vem parte da confusão.
Mark Ransom
2
@KartikAnand: Basicamente, sim. O intérprete sabe qual é qual e trata-os apropriadamente.
NPE
1
@Dubslow: você pode fazer isso usando o módulo itertools . Para o seu caso, utilize a função islice , com start=1, stop=None. Isso evitará quaisquer cópias e usará avaliação preguiçosa (no seu caso, acesso preguiçoso à lista original).
Spiros
66

Ao especificar ano lado esquerdo do =operador, você está usando a atribuição normal do Python , que altera o nome ano contexto atual para apontar para o novo valor. Isso não altera o valor anterior para o qual aestava apontando.

Ao especificar a[0:2]no lado esquerdo do =operador, você está dizendo ao Python que deseja usar Slice Assignment . Atribuição de fatia é uma sintaxe especial para listas, onde você pode inserir, excluir ou substituir o conteúdo de uma lista:

Inserção :

>>> a = [1, 2, 3]
>>> a[0:0] = [-3, -2, -1, 0]
>>> a
[-3, -2, -1, 0, 1, 2, 3]

Exclusão :

>>> a
[-3, -2, -1, 0, 1, 2, 3]
>>> a[2:4] = []
>>> a
[-3, -2, 1, 2, 3]

Substituição :

>>> a
[-3, -2, 1, 2, 3]
>>> a[:] = [1, 2, 3]
>>> a
[1, 2, 3]

Nota:

O comprimento da fatia pode ser diferente do comprimento da sequência atribuída, alterando assim o comprimento da sequência alvo, se a sequência alvo permitir. - fonte

Slice Assignment fornece uma função semelhante à Tuple Unpacking . Por exemplo, a[0:1] = [4, 5]é equivalente a:

# Tuple Unpacking
a[0], a[1] = [4, 5]

Com Tuple Unpacking, você pode modificar listas não sequenciais:

>>> a
[4, 5, 3]
>>> a[-1], a[0] = [7, 3]
>>> a
[3, 5, 7]

No entanto, a descompactação da tupla é limitada à substituição, pois você não pode inserir ou remover elementos.

Antes e depois de todas essas operações, aé a mesma lista exata. Python simplesmente fornece uma ótima sintaxe para modificar uma lista no local.

Casey Kuball
fonte
6
Semelhante, mas não idêntico, pois você pode ter um número desigual de elementos à esquerda e à direita.
Mark Ransom
@MarkRansom Esse é um ponto excelente, adicionei mais informações para tornar isso óbvio.
Casey Kuball
2
É a[:] = some_listequivalente a a = some_list[:]ou a = some_list?
jadkik94
2
@ jadkik94 Nem. a[:] = some_listdefine cada elemento de apara ser aquele de some_list. Fazer qualquer um dos que você mencionou mudaria o que aé. Por exemplo: a = [1, 2, 3] b = a a[:] = [4, 5, 6] a is b. A última linha seria False se alterasse ao valor de, em vez de alterá-lo.
Casey Kuball
@Darthfett Interessante, eu descobri o contrário :) Obrigado.
jadkik94
25

Já me deparei com a mesma pergunta antes e ela está relacionada à especificação da linguagem. De acordo com as instruções de atribuição ,

  1. Se o lado esquerdo da atribuição for assinatura, o Python irá chamar __setitem__esse objeto. a[i] = xé equivalente a a.__setitem__(i, x).

  2. Se o lado esquerdo da atribuição for uma fatia, o Python também chamará __setitem__, mas com argumentos diferentes: a[1:4]=[1,2,3]é equivalente a a.__setitem__(slice(1,4,None), [1,2,3])

É por isso que a fatia da lista no lado esquerdo de '=' se comporta de maneira diferente.

Stan
fonte
4

Ao fatiar no lado esquerdo de uma operação de atribuição, você especifica a quais itens atribuir.

Fraxel
fonte