Python List Comprehension
Nas aulas anteriores, você escreveu muitas vezes a mesma receita: começar com uma lista vazia, percorrer outra lista com for e ir fazendo append. "As notas de quem passou", "os itens em maiúsculas", "os preços com frete". A receita é tão comum que o Python tem um jeito de escrevê-la em uma linha só: a list comprehension (algo como "compreensão de lista"; em português, muita gente diz só "comprehension" mesmo). Ela é uma das coisas que mais deixam o código com cara de Python, e também uma das mais fáceis de exagerar.
- A ler e escrever
[expressão for item in lista], comparando com ofor+append. - A transformar cada item (maiúsculas, contas, tamanhos) e a usar
range(). - A filtrar com
ifno fim: só os itens que passam no teste. - A escolher entre dois valores com
if/elseno começo, e por que os doisifficam em lugares diferentes. - Quando não usar: para fazer coisas (como
print), quando fica comprida demais, quando precisa debreak. - Os erros reais: o
elseno fim, oifsemelseno começo, a lista deNone, a variável que não existe fora e os parênteses no lugar dos colchetes.
A ideia: descrever a lista em vez de montá-la
Pense em duas maneiras de pedir uma coisa a alguém:
- Passo a passo: "pegue uma folha em branco; para cada item da minha lista, escreva o item em letras maiúsculas na folha".
- Descrevendo o resultado: "quero os itens da minha lista, em maiúsculas".
As duas dão a mesma folha. O for com append é o passo a passo; a list comprehension é a descrição. Ela se lê quase como a frase: [item.upper() for item in compras], "o item em maiúsculas, para cada item em compras".
1. Do for para a comprehension
Primeiro, o jeito que você já conhece:
compras = ["arroz", "feijão", "café"]
maiusculas = []
for item in compras:
maiusculas.append(item.upper())
print(maiusculas)
$ python3 lc_for.py ['ARROZ', 'FEIJÃO', 'CAFÉ']
Agora a mesma coisa, em list comprehension:
compras = ["arroz", "feijão", "café"] maiusculas = [item.upper() for item in compras] print(maiusculas) print(compras)
$ python3 lc.py ['ARROZ', 'FEIJÃO', 'CAFÉ'] ['arroz', 'feijão', 'café']
- [ … ]
- Os colchetes de fora dizem: "o resultado é uma lista". Eles fazem o papel do
maiusculas = []. - item.upper()
- A expressão: o que vai entrar na lista nova para cada item. Faz o papel do que estava dentro do
append(…). Vem primeiro, embora seja calculada a cada volta. - for item in compras
- O mesmo
forde sempre, só que sem os dois-pontos e sem bloco: diz de onde vêm os itens e o nome de cada um.
Repare na última linha da saída: a lista compras não mudou. A comprehension sempre cria uma lista nova; a original fica como estava.
for + append | List comprehension |
|---|---|
maiusculas = [] | maiusculas = [item.upper() for item in compras] |
for item in compras: | |
maiusculas.append(item.upper()) |
2. Transformando cada item
A expressão do começo pode ser qualquer coisa que dê um valor: uma conta, uma função, um método. E o for pode percorrer qualquer coisa que um for comum percorreria, como um range() (aula range()):
quadrados = [n * n for n in range(1, 6)] print(quadrados) precos = [4.50, 12.00, 8.75] com_frete = [preco + 0.50 for preco in precos] print(com_frete) tamanhos = [len(item) for item in ["arroz", "feijão", "café"]] print(tamanhos)
$ python3 lc_range.py [1, 4, 9, 16, 25] [5.0, 12.5, 9.25] [5, 6, 4]
[n * n for n in range(1, 6)]: para cadande 1 a 5, o quadrado den.[preco + 0.50 for preco in precos]: cada preço com R$ 0,50 a mais. Compare com oforcomrange(len(…))da aula Alterando Itens: lá a própria lista mudava; aqui nasce uma lista nova e a original fica intacta.[len(item) for item in …]: o tamanho de cada item.
Missão: o programa monta os preços com frete usando for e append. Troque as três linhas do meio por uma list comprehension que dê o mesmo resultado.
precos = [4.50, 12.00, 8.75]
com_frete = []
for preco in precos:
com_frete.append(preco + 0.50)
print(com_frete)
precos = [4.50, 12.00, 8.75] com_frete = [preco + 0.50 for preco in precos] print(com_frete)
3. Filtrando: if no fim
A professora quer só as notas de quem passou. Um if (aula if) no fim da comprehension funciona como uma peneira: o item só entra na lista nova se o teste der verdadeiro.
notas = [7.0, 4.5, 8.5, 6.0, 9.5, 5.0]
aprovadas = [nota for nota in notas if nota >= 6]
print(aprovadas)
turma = ["Ana", "Bruno", "Alice", "Carla", "Artur"]
com_a = [nome for nome in turma if nome.startswith("A")]
print(com_a)
$ python3 lc_if.py [7.0, 8.5, 6.0, 9.5] ['Ana', 'Alice', 'Artur']
- nota
- A expressão: o próprio item, sem transformar. (Dá para transformar e filtrar ao mesmo tempo:
[nota + 1 for nota in notas if nota >= 6].) - for nota in notas
- De onde vêm os itens.
- if nota >= 6
- A peneira. A nota 4.5 não passa, e simplesmente não entra na lista nova. Por isso a lista nova pode ser menor que a original.
Este é o "jeito de montar uma lista nova só com o que fica", que a aula Removendo Itens recomendou no lugar de remover itens dentro de um for. Ali levou quatro linhas; aqui, uma.
Missão: a lista aprovadas está recebendo todas as notas. Acrescente um if no fim da comprehension para ficarem só as notas 6 ou mais.
notas = [7.0, 4.5, 8.5, 6.0, 9.5, 5.0] aprovadas = [nota for nota in notas] print(aprovadas)
notas = [7.0, 4.5, 8.5, 6.0, 9.5, 5.0] aprovadas = [nota for nota in notas if nota >= 6] print(aprovadas)
4. Escolhendo entre dois valores: if/else no começo
E se a professora quiser, para cada aluno, a palavra "aprovado" ou "recuperação"? Agora ninguém fica de fora; o que muda é o valor que entra. Isso é o if em uma linha (aula if em uma Linha), e ele vai no começo, no lugar da expressão:
notas = [7.0, 4.5, 8.5, 6.0, 9.5, 5.0] situacao = ["aprovado" if nota >= 6 else "recuperação" for nota in notas] print(situacao)
$ python3 lc_ifelse.py ['aprovado', 'recuperação', 'aprovado', 'aprovado', 'aprovado', 'recuperação']
- "aprovado" if nota >= 6 else "recuperação"
- A expressão inteira: "
"aprovado"se a nota for 6 ou mais, senão"recuperação"". Ela sempre dá um valor, e esse valor entra na lista. - for nota in notas
- De onde vêm as notas. Seis notas, seis palavras: a lista nova tem o mesmo tamanho da original.
Os dois if, lado a lado
É aqui que quase todo mundo se confunde. Os dois if parecem iguais, mas fazem coisas diferentes e moram em lugares diferentes:
if no fim (filtro) | if/else no começo (escolha) | |
|---|---|---|
| Escrita | [nota for nota in notas if nota >= 6] | ["ok" if nota >= 6 else "rec" for nota in notas] |
| Pergunta que responde | "Este item entra na lista?" | "Qual valor entra no lugar deste item?" |
Tem else? | Não: quem não passa fica de fora | Sim, obrigatório: todo item precisa de um valor |
| Tamanho da lista nova | Igual ou menor | Sempre igual |
Dá para usar os dois juntos: ["ok" if nota >= 9 else "bom" for nota in notas if nota >= 6] primeiro peneira (só as notas 6 ou mais) e depois escolhe a palavra. Funciona, mas repare como já ficou difícil de ler. Guarde essa sensação para a seção 6.
5. Um uso do dia a dia: limpar o que foi digitado
A Ana digitou a lista de compras numa linha só, separada por vírgulas, com espaços sobrando, maiúsculas misturadas e até um item vazio. O split(",") (aula Métodos de Texto) corta o texto nas vírgulas, e a comprehension limpa cada pedaço:
digitado = " Arroz, feijão ,CAFÉ, , leite "
itens = [pedaco.strip().lower() for pedaco in digitado.split(",") if pedaco.strip()]
print(itens)
$ python3 lc_limpar.py ['arroz', 'feijão', 'café', 'leite']
digitado.split(","): os pedaços entre as vírgulas, com os espaços e tudo.if pedaco.strip(): a peneira. Um pedaço só de espaços, depois dostrip(), vira"", e um texto vazio conta como falso (aula Booleanos). Então ele fica de fora.pedaco.strip().lower(): o que entra, sem espaços nas pontas e em minúsculas.
Essa linha já está no limite do confortável. O mesmo programa, com for:
digitado = " Arroz, feijão ,CAFÉ, , leite "
itens = []
for pedaco in digitado.split(","):
pedaco = pedaco.strip()
if pedaco:
itens.append(pedaco.lower())
print(itens)
$ python3 lc_limpar2.py ['arroz', 'feijão', 'café', 'leite']
Mais comprido, mas cada linha faz uma coisa só, e o strip() é feito uma vez em vez de duas. Os dois estão certos; escolha o que você (e quem for ler depois) entende mais rápido.
6. Quando não usar
A list comprehension serve para uma coisa: criar uma lista a partir de outra. Fora disso, o for comum é melhor.
Para fazer algo, e não para criar uma lista
compras = ["arroz", "feijão", "café"] resultado = [print(item) for item in compras] print(resultado)
$ python3 lc_print.py arroz feijão café [None, None, None]
Os itens foram mostrados, mas, de quebra, o Python montou uma lista com o que cada print devolveu: None, três vezes (aula O Valor None). Uma lista inútil, criada só para ser jogada fora. Quando o objetivo é fazer algo com cada item (mostrar, gravar num arquivo, fazer append em outra lista), use o for:
compras = ["arroz", "feijão", "café"]
for item in compras:
print(item)
$ python3 mostrar.py arroz feijão café
A mesma saída, sem lista nenhuma sobrando.
Quando fica comprida ou cheia de condições
Uma comprehension pode ter vários for e vários if. Que lista esta linha monta?
compras = ["arroz", "feijão", "sal", "café"] consoantes = [letra.upper() for item in compras if len(item) > 3 for letra in item if letra not in "aeiouãé"] print(consoantes)
$ python3 lc_complicada.py ['R', 'R', 'Z', 'F', 'J', 'C', 'F']
Difícil dizer de cabeça. O mesmo com for:
compras = ["arroz", "feijão", "sal", "café"]
consoantes = []
for item in compras:
if len(item) > 3:
for letra in item:
if letra not in "aeiouãé":
consoantes.append(letra.upper())
print(consoantes)
$ python3 lc_complicada2.py ['R', 'R', 'Z', 'F', 'J', 'C', 'F']
Agora dá para ler: "para cada item com mais de 3 letras, para cada letra que não é vogal, guarde a letra em maiúscula". (O sal ficou de fora por ter só 3 letras.) Mesmo resultado, muito mais fácil de entender e de consertar.
| Situação | Use |
|---|---|
| Uma lista nova, transformando cada item | Comprehension: [x * 2 for x in lista] |
| Uma lista nova, só com alguns itens | Comprehension com if no fim |
| Uma lista nova, com um de dois valores para cada item | Comprehension com if/else no começo |
Fazer algo com cada item (print, gravar, contar) | for |
| Alterar a própria lista | for i in range(len(lista)) (aula Alterando Itens) |
Parar no meio (break) | for: a comprehension não tem break |
Não cabe numa linha, ou precisa de mais de um if ou de um for dentro de outro | for: quem lê agradece |
| Só somar, achar o maior, contar | sum(), max(), len() (às vezes com uma comprehension dentro: sum([p * 2 for p in precos])) |
Quando dá errado
else no fim
A professora queria "aprovado" ou "recuperação" e pôs o if/else no fim, onde mora o filtro:
notas = [7.0, 4.5, 8.5] situacao = ["aprovado" for nota in notas if nota >= 6 else "recuperação"] print(situacao)
$ python3 lc_erro_else.py
File "/home/ana/lc_erro_else.py", line 2
situacao = ["aprovado" for nota in notas if nota >= 6 else "recuperação"]
^^^^
SyntaxError: invalid syntax
Linha por linha:
File "/home/ana/lc_erro_else.py", line 2: o problema está na linha 2. SemTraceback: é um erro de escrita, e nada rodou.- Os
^^^^apontam para oelse: foi ali que o Python se perdeu. SyntaxError: invalid syntax: "sintaxe inválida". Oifdo fim é um filtro, e filtro não temelse; o Python não esperava aquela palavra ali.
Como consertar: com else, o if vai para o começo: ["aprovado" if nota >= 6 else "recuperação" for nota in notas].
if sem else no começo
notas = [7.0, 4.5, 8.5] aprovadas = [nota if nota >= 6 for nota in notas] print(aprovadas)
$ python3 lc_erro_ifsem.py
File "/home/ana/lc_erro_ifsem.py", line 2
aprovadas = [nota if nota >= 6 for nota in notas]
^^^^^^^^^^^^^^^^^
SyntaxError: expected 'else' after 'if' expression
Agora o erro é o contrário, e o Python 3.14 explica bem: "esperava else depois da expressão if". No começo, o if está escolhendo um valor, e precisa saber o que pôr quando o teste der falso. Os ^^^^ marcam a expressão inteira, nota if nota >= 6. Como consertar: se a ideia era filtrar, mova o if para o fim: [nota for nota in notas if nota >= 6]. Se era escolher, acrescente o else: [nota if nota >= 6 else 0 for nota in notas].
compras = ["arroz", "feijão", "café"] maiusculas = [item.upper() for item in compras] print(maiusculas) print(item)
$ python3 lc_vazamento.py
['ARROZ', 'FEIJÃO', 'CAFÉ']
Traceback (most recent call last):
File "/home/ana/lc_vazamento.py", line 4, in <module>
print(item)
^^^^
NameError: name 'item' is not defined. Did you mean: 'iter'?
Depois de um for comum, a variável do laço continua existindo, com o último valor. A da comprehension, não: o item só existe dentro dos colchetes. Fora deles, name 'item' is not defined. A dica Did you mean: 'iter'? é o Python procurando um nome parecido (iter é uma função embutida); aqui ela não ajuda. Isso é uma vantagem, na verdade: a comprehension não deixa variáveis soltas pelo programa. Como consertar: se você precisa do último item, use compras[-1].
notas = [7.0, 4.5, 8.5] aprovadas = (nota for nota in notas if nota >= 6) print(aprovadas) print(list(aprovadas))
$ python3 lc_parenteses.py <generator object <genexpr> at 0x7fe7e2b0cee0> [7.0, 8.5]
Sem erro, mas o print não mostrou uma lista: mostrou um gerador (generator), um objeto que vai produzindo os itens quando alguém pede, sem guardar todos de uma vez (o número depois do at é um endereço da memória e muda a cada execução). Com list() em volta, os itens aparecem. Geradores são muito úteis e têm aula própria: Geradores. Como consertar: para uma lista, colchetes: [nota for nota in notas if nota >= 6].
None
Você viu na seção 6: [print(item) for item in compras] mostra os itens e cria [None, None, None]. Se aparecer uma lista cheia de None, procure uma comprehension cuja expressão é uma função ou método que não devolve nada (print, append, sort…). Use um for comum.
Referência rápida
| Forma | O que faz | Exemplo |
|---|---|---|
[expr for x in seq] | Uma lista com expr calculada para cada x | [n * n for n in range(4)] → [0, 1, 4, 9] |
[x for x in seq if cond] | Só os itens em que cond é verdadeira | [n for n in notas if n >= 6] |
[a if cond else b for x in seq] | Para cada item, a ou b | ["ok" if n >= 6 else "rec" for n in notas] |
[expr for x in seq if cond] | Filtra e transforma | [n + 1 for n in notas if n >= 6] |
list(… for x in seq) / (… for x in seq) | Gerador (não é lista até passar pelo list()) | Aula Geradores |
| Sintoma | Causa provável |
|---|---|
SyntaxError: invalid syntax apontando o else | if/else no fim; com else, o if vai no começo |
SyntaxError: expected 'else' after 'if' expression | if no começo sem else; para filtrar, o if vai no fim |
Uma lista cheia de None | A expressão é um print (ou outro que devolve None): use for |
NameError com a variável da comprehension | Ela só existe dentro dos colchetes |
<generator object <genexpr> at 0x…> | Parênteses no lugar dos colchetes |
Exercícios
1. Complete a comprehension
Complete para criar a lista com o dobro de cada número:
2. O resultado
O que [n * 10 for n in range(3)] devolve? Escreva como o Python mostraria.
3. Só os baratos
Complete o filtro para ficarem só os preços menores que 10:
4. Qual destas linhas está escrita certo?
5. Com notas = [8, 3, 9, 5], quantos itens tem [n for n in notas if n >= 6]?
6. Em qual destas situações o for comum é melhor que uma list comprehension?
Missão final: a situação da turma
Missão: a professora quer, para cada nota, a palavra "aprovado" (6 ou mais) ou "recuperação". O programa dá SyntaxError. Ponha o if/else no lugar certo.
notas = [7.0, 4.5, 8.5, 6.0, 9.5, 5.0] situacao = ["aprovado" for nota in notas if nota >= 6 else "recuperação"] print(situacao)
notas = [7.0, 4.5, 8.5, 6.0, 9.5, 5.0] situacao = ["aprovado" if nota >= 6 else "recuperação" for nota in notas] print(situacao)
Existe também a mesma ideia para dicionários e conjuntos, com chaves em vez de colchetes: a aula Dict e Set Comprehension mostra como.
Resumo
[expressão for item in lista]é ofor+appendem uma linha: cria uma lista nova e não mexe na original.- A expressão do começo diz o que entra; o
fordiz de onde vêm os itens. ifno fim, semelse: filtra (quem não passa fica de fora).if/elseno começo: escolhe um de dois valores para cada item (a lista tem o mesmo tamanho).- Não use para fazer coisas (
print): você ganha uma lista deNone. Não use quando ficar comprida ou precisar debreak: oforé mais claro. - A variável da comprehension só existe dentro dos colchetes. Com parênteses, o resultado é um gerador, não uma lista.