Python Alterando Itens

A Ana chegou no mercado e lembrou que, em casa, ninguém toma café com açúcar. Ela não joga a lista de compras fora: pega a caneta, risca "café" e escreve "café sem açúcar" no mesmo lugar. As listas do Python deixam você fazer exatamente isso: trocar o valor de um item, sem mexer nos outros e sem criar uma lista nova. Nesta aula, você aprende a trocar um item, um pedaço da lista e todos os itens de uma vez.

Você vai aprender
  • A trocar um item com lista[índice] = novo_valor.
  • A fazer contas com um item: estoque[0] -= 1.
  • A achar um item pelo valor e trocá-lo (index() + atribuição).
  • A trocar uma fatia inteira, até por uma de outro tamanho.
  • A alterar todos os itens com for e range(len(…)), e por que o for sozinho não altera nada.
  • Por que dá para trocar um item de uma lista, mas não uma letra de um texto.
  • Os erros reais: IndexError na atribuição, o == que não troca nada, e a fatia que recebe um texto.

A ideia: riscar e escrever no mesmo lugar

Na aula Acessando Itens, você usou compras[2] para ler o item do armário número 2. Para trocar o que tem dentro desse armário, você usa o mesmo endereço, só que do lado esquerdo do =, exatamente como numa variável comum:

EscritaO que acontece
x = compras[2]Lê o item 2 e guarda o valor em x. A lista não muda.
compras[2] = xTroca o item 2 pelo valor de x. O valor que estava lá antes some.

Isso só é possível porque a lista é mutável (em inglês, mutable, "que pode mudar"): depois de criada, o conteúdo dela pode ser alterado. Nem todo tipo do Python é assim, como você vai ver na seção 5.


1. Trocando um item

compras = ["arroz", "feijão", "café", "leite"]
print("Antes: ", compras)
compras[2] = "café sem açúcar"
compras[-1] = "leite desnatado"
print("Depois:", compras)
compras[2]
O armário de índice 2 (o terceiro item, o café). Do lado esquerdo do =, ele é o lugar onde o novo valor vai ser guardado.
=
A atribuição de sempre: "guarde o valor da direita no lugar da esquerda".
"café sem açúcar"
O valor novo. Ele ocupa o lugar do antigo; o "café" deixa de existir na lista.
compras[-1]
Índices negativos também valem para trocar: aqui, o último item.
$ python3 trocar.py
Antes:  ['arroz', 'feijão', 'café', 'leite']
Depois: ['arroz', 'feijão', 'café sem açúcar', 'leite desnatado']

Repare no que não mudou: a lista continua com 4 itens, o arroz e o feijão continuam nos seus lugares, e a ordem é a mesma. Só os dois armários que você indicou receberam um conteúdo novo. (O espaço a mais em "Antes: " é só para as duas listas ficarem alinhadas na saída.)

Missão: troque o "café" da lista por "café descafeinado", com uma linha de atribuição antes do print (não reescreva a lista toda).

compras = ["arroz", "feijão", "café", "leite"]

print(compras)
compras = ["arroz", "feijão", "café", "leite"]
compras[2] = "café descafeinado"
print(compras)

2. Fazendo contas com um item

A Ana também controla o estoque da lojinha numa lista: quantas unidades ela tem de cada produto. Vendeu uma unidade? O item diminui 1. Chegou mercadoria? Aumenta.

estoque = [12, 5, 30]
print(estoque)
estoque[1] = estoque[1] - 1
estoque[0] -= 2
estoque[2] += 10
print(estoque)
$ python3 estoque.py
[12, 5, 30]
[10, 4, 40]
estoque[1] = estoque[1] - 1
Leia da direita para a esquerda: o Python lê o item 1 (5), faz a conta (5 − 1 = 4) e guarda o resultado de volta no item 1.
estoque[0] -= 2
A forma curta da mesma coisa (aula Operadores de Atribuição): "tire 2 do item 0". De 12 foi para 10.
estoque[2] += 10
"Some 10 ao item 2": de 30 para 40.

3. Achando o item pelo valor e trocando

Nem sempre você sabe o índice. A Ana quer trocar o "feijão" por "feijão preto", mas não lembra em que posição ele está. Juntando o que você aprendeu: primeiro pergunta se ele está lá (in), depois descobre o índice (index()), e só então troca:

compras = ["arroz", "feijão", "café", "leite"]
velho = "feijão"
novo = "feijão preto"
if velho in compras:
    posicao = compras.index(velho)
    compras[posicao] = novo
    print(f"Troquei {velho} por {novo} na posição {posicao}.")
print(compras)
$ python3 achar_trocar.py
Troquei feijão por feijão preto na posição 1.
['arroz', 'feijão preto', 'café', 'leite']

Sem o if velho in compras:, um item que não está na lista faria o index() parar o programa com ValueError (você viu esse erro em Acessando Itens). E lembre: o index() acha só a primeira ocorrência. Se o feijão aparecesse duas vezes, só o primeiro seria trocado.


4. Trocando uma fatia

Você pode trocar um pedaço da lista de uma vez: do lado esquerdo do = vai uma fatia (aula Acessando Itens), e do lado direito, uma lista com os itens novos.

>>> compras = ["arroz", "feijão", "café", "leite"]
>>> compras[1:3] = ["macarrão", "molho"]
>>> compras
['arroz', 'macarrão', 'molho', 'leite']

Os itens dos índices 1 e 2 (feijão e café) saíram, e os dois novos entraram no lugar deles. E se a quantidade for diferente? A fatia aceita: a lista cresce ou encolhe para caber.

>>> compras[1:2] = ["batata", "cenoura", "chuchu"]
>>> compras
['arroz', 'batata', 'cenoura', 'chuchu', 'molho', 'leite']
>>> len(compras)
6
>>> compras[1:4] = ["legumes"]
>>> compras
['arroz', 'legumes', 'molho', 'leite']
  • compras[1:2] = [...três itens...]: saiu um item (o macarrão, índice 1) e entraram três. A lista passou de 4 para 6 itens.
  • compras[1:4] = ["legumes"]: saíram três (batata, cenoura e chuchu) e entrou um. Voltou para 4.
Use com moderação

Trocar fatias de tamanhos diferentes é poderoso, mas fica difícil de ler: quem olha compras[1:4] = ["legumes"] precisa parar e contar. Para acrescentar ou tirar itens, existem jeitos mais claros, com nomes que dizem o que fazem: Adicionando Itens e Removendo Itens.


5. Lista muda, texto não

Se a lista deixa trocar um item, será que um texto deixa trocar uma letra? A Ana digitou o nome em minúsculas e quer a primeira letra maiúscula:

nome = "ana"
nome[0] = "A"
print(nome)
$ python3 texto_imutavel.py
Traceback (most recent call last):
  File "/home/ana/texto_imutavel.py", line 2, in <module>
    nome[0] = "A"
    ~~~~^^^
TypeError: 'str' object does not support item assignment

"Erro de tipo: o objeto str não aceita atribuição de item". Os textos (str) são imutáveis: depois de criados, não mudam. Dá para ler nome[0], mas não trocar. Para "mudar" um texto, você cria um texto novo e guarda na variável: nome = "A" + nome[1:], ou nome = nome.capitalize() (aula Métodos de Texto).

Lista (list)Texto (str)
Ler um item: x[0]PodePode
Fatiar: x[1:3]PodePode
Trocar um item: x[0] = …Pode (mutável)Não pode (imutável): TypeError

Essa diferença entre mutável e imutável explica muitas surpresas do Python; ela tem uma aula só para ela: Mutável × Imutável.


6. Alterando todos os itens

A professora vai dar meio ponto a mais para a turma toda. A primeira ideia é passar pelas notas com um for e somar 0.5 em cada uma:

notas = [7.0, 8.5, 6.0, 9.5]
for nota in notas:
    nota = nota + 0.5
print(notas)
$ python3 nota_errada.py
[7.0, 8.5, 6.0, 9.5]

Nada mudou, e nenhum erro apareceu. Por quê? A cada volta, o for põe na variável nota o valor do item da vez (7.0, depois 8.5…). A linha nota = nota + 0.5 calcula um valor novo e o guarda na variável nota, e só nela; o armário da lista continua com o valor antigo, e a lista nem fica sabendo. É como a Ana anotar a nota nova num papelzinho e não passar a limpo no diário.

Para mudar a lista, você precisa escrever no armário dela, notas[i] = …, e para isso precisa do índice. O range(len(notas)) (aula range()) dá exatamente os índices: 0, 1, 2, 3.

notas = [7.0, 8.5, 6.0, 9.5]
for i in range(len(notas)):
    notas[i] = notas[i] + 0.5
print(notas)
$ python3 nota_certa.py
[7.5, 9.0, 6.5, 10.0]
len(notas)
4, o número de notas.
range(len(notas))
Os números 0, 1, 2, 3: os índices válidos da lista.
for i in …
A cada volta, i é um índice. O nome i (de "índice") é um costume muito comum para isso.
notas[i] = notas[i] + 0.5
Lê o item i, soma 0.5 e guarda de volta no item i. Agora sim a lista muda.

Existe também um jeito de criar uma lista nova com todos os valores transformados, em uma linha só: a List Comprehension. E a aula Percorrendo uma Lista mostra o enumerate(), que dá o índice e o item ao mesmo tempo.


Formas diferentes de alterar

Você querEscrevaObservação
Trocar o item de um índice que você conhecelista[i] = novoO jeito básico; o índice tem de existir
Fazer uma conta com um itemlista[i] += 1Igual a lista[i] = lista[i] + 1, mais curto
Trocar um item pelo valorif velho in lista: + lista[lista.index(velho)] = novoTroca só a primeira ocorrência
Trocar vários itens seguidoslista[a:b] = [x, y]Do lado direito, sempre uma lista; pode mudar o tamanho
Alterar todos os itensfor i in range(len(lista)): + lista[i] = …Muda a própria lista. for item in lista: item = … não muda
Ter uma lista nova com tudo transformado[x + 0.5 for x in lista]A lista original fica como estava (aula List Comprehension)

Quando dá errado

IndexError: trocar um armário que não existe

A Ana quer acrescentar o sabão no fim da lista e pensou: "a lista tem 4 itens, então o próximo é o índice 4":

compras = ["arroz", "feijão", "café", "leite"]
compras[4] = "sabão"
print(compras)
$ python3 atribuir_fora.py
Traceback (most recent call last):
  File "/home/ana/atribuir_fora.py", line 2, in <module>
    compras[4] = "sabão"
    ~~~~~~~^^^
IndexError: list assignment index out of range

Linha por linha:

  • File "/home/ana/atribuir_fora.py", line 2: o problema está na linha 2.
  • As marcas ~~~~~~~^^^ apontam para compras[4]: o lugar onde a Ana tentou guardar o valor.
  • IndexError: list assignment index out of range: "erro de índice: índice da atribuição fora do intervalo". Repare na palavra assignment ("atribuição"): ao ler um índice que não existe, a mensagem é list index out of range; ao escrever, é esta.

A atribuição só troca o que já existe: os armários são 0, 1, 2 e 3, e não aparece um armário novo só porque você escreveu num número a mais. Como consertar: para pôr um item no fim, use compras.append("sabão"), que é o assunto da próxima aula, Adicionando Itens.

== no lugar de =: nada muda e nada reclama
compras = ["arroz", "feijão", "café", "leite"]
compras[1] == "lentilha"
print(compras)
$ python3 igual_duplo.py
['arroz', 'feijão', 'café', 'leite']

O feijão continua lá. Com dois sinais de igual, a linha 2 não é uma troca: é uma pergunta ("o item 1 é igual a lentilha?", aula Comparações). O Python responde False e joga a resposta fora, porque ninguém a guardou nem mostrou. No modo interativo dá para ver a resposta que se perdeu:

>>> compras[1] == "lentilha"
False

Como consertar: para trocar, um sinal de igual: compras[1] = "lentilha". Regra para lembrar: = guarda, == pergunta.

Uma fatia que recebe um texto

A Ana queria trocar o primeiro item por "uva", mas usou uma fatia e esqueceu os colchetes do lado direito:

>>> compras = ["arroz", "legumes", "molho", "leite"]
>>> compras[0:1] = "uva"
>>> compras
['u', 'v', 'a', 'legumes', 'molho', 'leite']

Nenhum erro, e a uva virou três itens: 'u', 'v' e 'a'. A fatia espera do lado direito uma sequência de itens, e um texto é uma sequência de letras; então cada letra virou um item (como no list("uva") da aula Listas). Com um número, que não é uma sequência, aí sim vem um erro:

>>> compras[0:1] = 5
Traceback (most recent call last):
  File "<python-input-3>", line 1, in <module>
    compras[0:1] = 5
    ~~~~~~~^^^^^
TypeError: must assign iterable to extended slice

"É preciso atribuir um iterável (algo com vários itens) à fatia". Como consertar: para trocar um item só, use o índice, sem fatia: compras[0] = "uva". Se for mesmo uma fatia, ponha a lista do lado direito: compras[0:1] = ["uva"].

O for que "altera" e não altera

Você viu na seção 6: for nota in notas: nota = nota + 0.5 roda sem erro e deixa a lista igualzinha. Se a lista "não mudou" depois de um for, procure uma linha que atribui à variável do laço (nota = …) em vez de ao item da lista (notas[i] = …).


Referência rápida

EscritaO que faz
lista[i] = valorTroca o item de índice i (que tem de existir) por valor
lista[-1] = valorTroca o último item
lista[i] += n / -= / *=Faz a conta com o item e guarda de volta
lista[lista.index(velho)] = novoTroca a primeira ocorrência de velho (erro se não houver)
lista[a:b] = [x, y, …]Troca os itens de a até antes de b pelos novos; a lista pode crescer ou encolher
for i in range(len(lista)): lista[i] = …Altera todos os itens da própria lista
SintomaCausa provável
IndexError: list assignment index out of rangeAtribuiu a um índice que não existe (para acrescentar no fim, use append)
A lista não mudou, sem erro== no lugar de =, ou atribuição à variável do for
Apareceram letras soltas como itensAtribuiu um texto a uma fatia: lista[0:1] = "uva"
TypeError: must assign iterable to extended sliceAtribuiu um número (ou outro valor sem itens) a uma fatia
TypeError: 'str' object does not support item assignmentTentou trocar uma letra de um texto; textos são imutáveis

Exercícios

1. Trocando o primeiro

Complete para trocar o primeiro aluno da lista por "Ana Paula":

alunos[] "Ana Paula"

2. Vendi duas

Complete o operador para tirar 2 unidades do item 0 do estoque:

estoque[0] 2

3. Quantos itens?

Com lista = [1, 2, 3, 4, 5], depois de lista[1:4] = [0], quanto vale len(lista)?

4. A lista compras tem 3 itens. O que acontece com compras[3] = "sal"?

5. Depois de precos = [10, 20] e for p in precos: p = p * 2, quanto vale precos?

6. Qual destes dá erro?

Missão final: meio ponto para a turma

Missão: a professora quer somar 0.5 em todas as notas, mas o programa mostra a lista sem mudança nenhuma. Conserte o laço para ele alterar a própria lista (dica: range(len(notas)) e notas[i]).

notas = [7.0, 8.5, 6.0, 9.5]
for nota in notas:
    nota = nota + 0.5
print(notas)
notas = [7.0, 8.5, 6.0, 9.5]
for i in range(len(notas)):
    notas[i] = notas[i] + 0.5
print(notas)

Resumo

  • Listas são mutáveis: lista[i] = novo troca um item no lugar, sem mexer nos outros.
  • Do lado direito do =, lista[i] lê; do lado esquerdo, escreve.
  • Contas com um item: estoque[0] -= 1.
  • Para trocar pelo valor: in para conferir, index() para achar, atribuição para trocar.
  • lista[a:b] = [...] troca uma fatia, até por outra de tamanho diferente. Do lado direito, sempre uma lista.
  • Atribuir a um índice que não existe dá IndexError: a atribuição não aumenta a lista.
  • for item in lista: item = … não altera a lista; use for i in range(len(lista)): lista[i] = ….
  • Textos são imutáveis: nome[0] = "A" dá TypeError.