Lambda em Python: O que É e Como Funciona | Python Brasil
Aprenda lambda em Python: funções anônimas, uso com map/filter/sorted, closures, boas práticas e quando evitar. Guia completo com exemplos.
O que é Lambda?
Uma lambda em Python é uma função anônima — uma função sem nome — definida em uma única expressão. A palavra-chave lambda cria um objeto de função sem precisar do bloco def, tornando-a ideal para funções curtas usadas em um único lugar.
A sintaxe completa é:
lambda parametros: expressao
A expressao é avaliada e retornada automaticamente — não existe return explícito. Lambda suporta todos os tipos de parâmetros que def suporta: posicionais, com valor padrão, *args e **kwargs.
Exemplos básicos
# Equivalência entre def e lambda
def dobro(x):
return x * 2
dobro_lambda = lambda x: x * 2
print(dobro(5)) # 10
print(dobro_lambda(5)) # 10
# Múltiplos parâmetros
soma = lambda a, b: a + b
print(soma(3, 7)) # 10
# Parâmetro com valor padrão
potencia = lambda base, exp=2: base ** exp
print(potencia(3)) # 9
print(potencia(2, 10)) # 1024
# Condicional (ternário)
sinal = lambda x: 'positivo' if x > 0 else ('negativo' if x < 0 else 'zero')
print(sinal(-5)) # negativo
print(sinal(0)) # zero
Uso com funções built-in
O principal poder das lambdas aparece quando combinadas com map(), filter(), sorted() e max()/min():
numeros = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# filter — mantém elementos onde a lambda retorna True
pares = list(filter(lambda x: x % 2 == 0, numeros))
# [2, 4, 6, 8, 10]
# map — transforma cada elemento
cubos = list(map(lambda x: x ** 3, numeros))
# [1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
# sorted com key — ordena por critério personalizado
alunos = [('Ana', 8.5), ('Bruno', 7.2), ('Carla', 9.1), ('Diego', 7.2)]
por_nota = sorted(alunos, key=lambda aluno: aluno[1], reverse=True)
# [('Carla', 9.1), ('Ana', 8.5), ('Bruno', 7.2), ('Diego', 7.2)]
# Ordenacao multipla: nota decrescente, nome crescente como desempate
por_nota_nome = sorted(alunos, key=lambda a: (-a[1], a[0]))
# [('Carla', 9.1), ('Ana', 8.5), ('Bruno', 7.2), ('Diego', 7.2)]
# max/min com key
mais_novo = min([{'nome': 'Ana', 'idade': 25}, {'nome': 'Bruno', 'idade': 22}],
key=lambda p: p['idade'])
print(mais_novo['nome']) # Bruno
functools.reduce com lambda
from functools import reduce
numeros = [1, 2, 3, 4, 5]
# Soma acumulada
total = reduce(lambda acc, x: acc + x, numeros)
print(total) # 15
# Produto
produto = reduce(lambda acc, x: acc * x, numeros)
print(produto) # 120
# Achatar uma lista de listas (flatten)
listas = [[1, 2], [3, 4], [5]]
plana = reduce(lambda acc, x: acc + x, listas)
print(plana) # [1, 2, 3, 4, 5]
# Encontrar o máximo sem usar max()
maximo = reduce(lambda a, b: a if a > b else b, numeros)
print(maximo) # 5
Closures com lambda
Lambdas capturam variáveis do escopo ao redor (closure), mas esse comportamento pode gerar surpresas:
# Closure funciona corretamente com parâmetro com valor padrão
def criar_multiplicadores(lista_n):
return [lambda x, n=n: x * n for n in lista_n]
dobro, triplo, quadruplo = criar_multiplicadores([2, 3, 4])
print(dobro(5)) # 10
print(triplo(5)) # 15
print(quadruplo(5)) # 20
# ARMADILHA CLÁSSICA: captura por referência, não por valor
multiplicadores_errados = [lambda x: x * n for n in [2, 3, 4]]
# Todas as lambdas capturam a mesma variável 'n' — que ao final do loop vale 4
print(multiplicadores_errados[0](5)) # 20 (esperado: 10!)
print(multiplicadores_errados[1](5)) # 20 (esperado: 15!)
print(multiplicadores_errados[2](5)) # 20 (correto por acaso)
# Solucao: use o parametro padrao para capturar o valor no momento da criacao
multiplicadores_corretos = [lambda x, n=n: x * n for n in [2, 3, 4]]
print(multiplicadores_corretos[0](5)) # 10
print(multiplicadores_corretos[1](5)) # 15
Lambda com defaultdict
from collections import defaultdict
# defaultdict aceita um callable sem argumentos como factory
contagem = defaultdict(lambda: 0)
for letra in 'abracadabra':
contagem[letra] += 1
print(dict(contagem))
# {'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1}
# Factory mais complexa
inventario = defaultdict(lambda: {'quantidade': 0, 'preco': 0.0})
inventario['maçã']['quantidade'] += 10
inventario['maçã']['preco'] = 2.50
print(inventario['maçã']) # {'quantidade': 10, 'preco': 2.5}
print(inventario['banana']) # {'quantidade': 0, 'preco': 0.0}
Lambda vs módulo operator
O módulo operator oferece equivalentes funcionais de operadores Python, geralmente mais rápidos que lambdas:
import operator
dados = [{'nome': 'Ana', 'idade': 25}, {'nome': 'Bruno', 'idade': 22}]
# Lambda vs operator.itemgetter
sorted(dados, key=lambda d: d['nome']) # lambda
sorted(dados, key=operator.itemgetter('nome')) # operator (mais rápido)
# operator.attrgetter para objetos
from dataclasses import dataclass
@dataclass
class Produto:
nome: str
preco: float
produtos = [Produto('Café', 15.0), Produto('Açúcar', 8.5)]
mais_barato = min(produtos, key=operator.attrgetter('preco'))
# equivalente a: key=lambda p: p.preco
# operator.methodcaller
palavras = ['banana', 'MAÇÃ', 'cereja']
ordenadas = sorted(palavras, key=operator.methodcaller('lower'))
# equivalente a: key=lambda s: s.lower()
Lambda em callbacks de GUI (tkinter)
import tkinter as tk
def criar_interface():
janela = tk.Tk()
janela.title("Calculadora Simples")
resultado = tk.StringVar(value="0")
tk.Label(janela, textvariable=resultado, font=('Arial', 24)).pack()
# Lambda captura o valor atual de 'n' no momento de criação do botão
for n in range(10):
tk.Button(
janela,
text=str(n),
command=lambda v=n: resultado.set(str(v))
).pack(side=tk.LEFT)
# Sem o truque n=n, todas as lambdas apontariam para n=9
tk.mainloop()
Limitações do lambda
Lambda tem restrições importantes que def não tem:
# 1. Apenas UMA expressão — sem múltiplas instruções
# f = lambda x: x = x + 1; return x # SyntaxError
# 2. Sem docstring
def com_doc(x):
"""Retorna o dobro de x."""
return x * 2
# lambda x: x * 2 — não tem como adicionar docstring
# 3. Sem anotações de tipo
# f = lambda x: int -> int: x * 2 # SyntaxError
# 4. Sem statements: if sem else, while, for, try/except
# f = lambda x: if x > 0: x else -x # SyntaxError (sem ternário)
# f = lambda x: x if x > 0 else -x # OK — isso é uma expressao
# 5. Sem yield (não pode ser generator)
# f = lambda: yield 42 # SyntaxError
Anti-padrões comuns
# EVITE: atribuir lambda a variável — use def
# Ruim (PEP 8 desaprova explicitamente)
quadrado = lambda x: x ** 2
# Bom
def quadrado(x):
return x ** 2
# EVITE: lambda que chama outra função sem necessidade
# Ruim
resultado = map(lambda x: str(x), numeros)
# Bom — passe a função diretamente
resultado = map(str, numeros)
# EVITE: lambda complexa e ilegível
# Ruim
processar = lambda dados: {k: v for k, v in dados.items() if v is not None and isinstance(v, (int, float))}
# Bom — use def com nome descritivo
def filtrar_numericos(dados):
"""Retorna apenas os pares chave-valor com valores numéricos."""
return {k: v for k, v in dados.items()
if v is not None and isinstance(v, (int, float))}
Orientação do PEP 8
O guia de estilo oficial do Python é explícito sobre lambda:
“Always use a def statement instead of an assignment statement that binds a lambda expression directly to an identifier.”
Em outras palavras: se você precisa dar um nome à função, use def. A única exceção razoável são casos onde a lambda é passada diretamente como argumento — especialmente em key=, command= ou callbacks similares.
Quando usar lambda: resumo
Use lambda quando:
- A função tem uma única expressão simples.
- É usada apenas uma vez, diretamente como argumento.
- O contexto deixa claro o propósito (ex.:
key=lambda x: x[1]).
Evite lambda quando:
- A lógica tem mais de uma etapa ou precisa de tratamento de erros.
- A função será reutilizada ou precisa de documentação.
- A expressão for longa o suficiente para dificultar a leitura.
Termos Relacionados
- List Comprehension — Alternativa mais pythonica a
map/filtercom lambda - Decorators — Funções que modificam outras funções em Python
- Generators — Funções que produzem valores sob demanda com
yield