Lambdas e compreensões
Esses três recursos têm algo em comum: permitem expressar ideias que de outra forma exigiriam várias linhas em uma única expressão legível. Bem usados, deixam o código mais curto e claro. Mal usados, tornam-no ilegível. Este capítulo aborda quando recorrer a cada um deles e quando parar.
Funções lambda
Uma lambda é uma função sem nome, de uma única expressão. Você a cria com a palavra-chave lambda. Sua real utilidade é que você pode escrevê-la inline, exatamente onde precisa, sem antes definir uma função nomeada. É isso que a torna útil com sorted().
double = lambda x: x * 2
double(5) # 10Isso equivale a:
def double(x):
return x * 2Na maioria dos casos, use def. Lambdas têm uma vantagem real: você pode escrevê-las inline, exatamente onde precisa, sem nomeá-las. É isso que as torna úteis com sorted(), map() e filter():
players = [("Ana", 87), ("Bruno", 74), ("Carla", 92)]
sorted(players, key=lambda p: p[1]) # ordena por pontuação (crescente)
sorted(players, key=lambda p: p[1], reverse=True) # ordena por pontuação (decrescente)Sem uma lambda, você teria que definir uma função nomeada apenas para o argumento key=. A lambda mantém a intenção local e visível.
Lambdas podem receber múltiplos argumentos:
add = lambda a, b: a + b
add(3, 4) # 7Quando usar uma lambda: apenas quando for uma expressão simples usada em um único lugar. Se estiver ficando complexa, ou se você precisar reutilizá-la, escreva um def adequado. Uma lambda que se estende por vários operadores ou requer condicionais geralmente é um sinal para mudar para def.
Compreensões de lista
A transformação mais comum em Python: pegar uma sequência, fazer algo com cada item e obter uma nova lista. Uma compreensão de lista faz isso em uma linha legível: [expressão for item in iterável]. Você também pode adicionar um filtro com if.
O jeito longo:
numbers = [1, 2, 3, 4, 5]
squares = []
for n in numbers:
squares.append(n ** 2)A compreensão de lista:
squares = [n ** 2 for n in numbers]A estrutura é sempre a mesma: [expressão for item in iterável].
scores = [87, 42, 96, 55, 71]
scaled = [s * 1.1 for s in scores] # aplica um bônus de 10%
as_grades = [f"{s}/100" for s in scores] # formata cada umFiltrando com uma condição
Adicione uma cláusula if para incluir apenas itens que passem em um teste. O resultado é uma nova lista apenas com os itens em que a condição é True.
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
evens = [n for n in numbers if n % 2 == 0] # [2, 4, 6, 8]
odds = [n for n in numbers if n % 2 != 0] # [1, 3, 5, 7]scores = [87, 42, 96, 55, 71, 38]
passing = [s for s in scores if s >= 60] # [87, 96, 71]
failing = [s for s in scores if s < 60] # [42, 55, 38]Compreensões aninhadas
Você pode aninhar compreensões para achatar uma lista de listas em uma única lista. Leia da esquerda para a direita: para cada linha, para cada item dessa linha, inclua o item.
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat = [item for row in matrix for item in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]Leia da esquerda para a direita: para cada linha em matrix, para cada item em linha, inclua item.
Compreensões aninhadas podem ficar confusas rapidamente. Se levar mais que um instante para entender, escreva os laços explicitamente.
Compreensões de dicionário
Compreensões de dicionário constroem um dicionário em uma única expressão, usando a mesma ideia das compreensões de lista: {chave: valor for item in iterável}. Adicione um filtro com if, assim como nas compreensões de lista.
names = ["ana", "bruno", "carla"]
scores = [87, 74, 92]
score_map = {name: score for name, score in zip(names, scores)}
# {"ana": 87, "bruno": 74, "carla": 92}Com um filtro:
passing = {name: score for name, score in score_map.items() if score >= 80}
# {"ana": 87, "carla": 92}words = ["maçã", "banana", "cereja"]
word_lens = {word: len(word) for word in words}
# {"maçã": 4, "banana": 6, "cereja": 6}Compreensões de conjunto
Compreensões de conjunto constroem um conjunto em uma única expressão, com chaves e sem dois-pontos. Como o resultado é um conjunto, duplicatas são removidas automaticamente.
words = ["maçã", "banana", "cereja", "maçã"]
unique = {w.lower() for w in words} # {"maçã", "banana", "cereja"}Use compreensões de conjunto quando quiser valores únicos e não se importar com a ordem.
Expressões geradoras
Geradores se parecem com compreensões de lista, mas com parênteses em vez de colchetes. A diferença-chave: uma compreensão de lista constrói toda a lista em memória de uma vez. Um gerador produz valores um de cada vez, apenas quando necessário. Para sequências grandes, isso usa muito menos memória.
squares_gen = (n ** 2 for n in range(1000000))total = sum(n ** 2 for n in range(1000000)) # sum() consome o geradorAo passar um gerador diretamente para uma função como sum(), max(), min() ou any(), você pode dispensar os parênteses extras:
total = sum(n ** 2 for n in range(1000)) # um par de parênteses, não doisPara a maioria do código do dia a dia, compreensões de lista servem bem. Use geradores quando estiver processando grandes conjuntos de dados ou dados em streaming, onde manter tudo em memória seria um desperdício.
zip()
zip() combina itens de duas ou mais sequências para que você possa percorrê-las em paralelo. Para na sequência mais curta. É a forma limpa de evitar gerenciar índices quando duas listas se correspondem.
names = ["Ana", "Bruno", "Carla"]
scores = [87, 74, 92]
for name, score in zip(names, scores):
print(f"{name}: {score}")
# Ana: 87
# Bruno: 74
# Carla: 92zip() para na sequência mais curta. Se suas sequências puderem ter tamanhos diferentes, use itertools.zip_longest() com um valor de preenchimento.
Para converter de volta de uma lista zipada de pares para duas listas separadas, use zip(*pairs):
pairs = [("Ana", 87), ("Bruno", 74), ("Carla", 92)]
names, scores = zip(*pairs)
# names = ("Ana", "Bruno", "Carla")
# scores = (87, 74, 92)O que o * faz aqui?
*pairs desempacota a lista em argumentos separados: zip(*pairs) se torna zip(("Ana", 87), ("Bruno", 74), ("Carla", 92)). O operador * é abordado no capítulo Funções.
zip() também é a forma limpa de iterar múltiplas sequências em paralelo sem gerenciar índices manualmente:
before = [10, 20, 30]
after = [15, 18, 35]
for b, a in zip(before, after):
change = a - b
print(f"{b} -> {a} ({'+' if change >= 0 else ''}{change})")map() e filter()
map() e filter() são ferramentas mais antigas, de estilo funcional, que fazem o que as compreensões fazem. Você as verá em código mais antigo, então vale a pena saber o que significam. Prefira compreensões para código novo; são mais legíveis para a maioria dos desenvolvedores Python.
numbers = [1, 2, 3, 4, 5]
list(map(lambda x: x ** 2, numbers)) # [1, 4, 9, 16, 25]
list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4]Prefira compreensões; são mais legíveis para a maioria dos desenvolvedores Python. Use map() quando você já tem uma função nomeada que existe:
strings = ["1", "2", "3"]
numbers = list(map(int, strings)) # [1, 2, 3] (mais limpo que uma compreensão aqui)Na prática
Filtre uma lista de jogadores pelas pontuações aprovadas, ordene por pontuação com sorted e uma lambda, depois imprima com posições enumeradas:
players = [
{"name": "Ana", "score": 87},
{"name": "Bruno", "score": 42},
{"name": "Carla", "score": 96},
{"name": "Diego", "score": 55},
]
passing = [p for p in players if p["score"] >= 60]
ranked = sorted(passing, key=lambda p: p["score"], reverse=True)
score_map = {p["name"]: p["score"] for p in ranked}
for i, (name, score) in enumerate(score_map.items(), start=1):
print(f"{i}. {name}: {score}")
