Ao trabalhar com listas de objetos em Python, é comum precisar ordenar, agrupar, filtrar ou transformar dados com base em um atributo. A solução mais conhecida é usar uma função lambda, como lambda item: item.nome. Ela funciona bem, mas existe uma alternativa da biblioteca padrão que torna a intenção mais explícita: operator.attrgetter. Essa função cria um callable especializado em recuperar atributos, inclusive atributos aninhados, e pode ser reutilizada em várias operações.
Neste guia, você aprenderá como usar operator.attrgetter no Python, quando ele é melhor do que uma lambda, como acessar vários atributos, como trabalhar com caminhos aninhados, quais erros podem ocorrer e como aplicar a técnica em código real. O objetivo não é substituir todas as lambdas, e sim escolher a ferramenta que deixa a regra mais legível.
O que é operator.attrgetter
attrgetter pertence ao módulo operator, que oferece funções equivalentes a diversas operações da linguagem. Em vez de escrever uma função manual para buscar um atributo, você informa o nome desejado e recebe uma função pronta.
from operator import attrgetter
obter_nome = attrgetter("nome")
print(obter_nome(usuario))O objeto devolvido por attrgetter é chamável. Isso significa que ele pode ser passado diretamente para parâmetros como key em sorted, min, max e itertools.groupby. Essa composição combina muito bem com conceitos apresentados no artigo sobre ordenação com sort e sorted.
Ordenando objetos por atributo
Considere uma classe que representa produtos:
from dataclasses import dataclass
from operator import attrgetter
@dataclass
class Produto:
nome: str
preco: float
estoque: int
produtos = [
Produto("Teclado", 180.0, 8),
Produto("Mouse", 90.0, 15),
Produto("Monitor", 1200.0, 4),
]
ordenados = sorted(produtos, key=attrgetter("preco"))O resultado é uma nova lista ordenada pelo preço. A chamada é equivalente a sorted(produtos, key=lambda p: p.preco). A diferença principal é semântica: attrgetter("preco") comunica diretamente que a chave é um atributo. Em bases de código maiores, isso reduz ruído e facilita a leitura.
Para ordenar em ordem decrescente, use reverse=True. Para encontrar o produto mais caro, combine com max:
mais_caro = max(produtos, key=attrgetter("preco"))Acessando atributos aninhados
Um dos recursos mais úteis é a notação com ponto. Se um pedido possui um cliente e o cliente possui um endereço, você pode acessar um caminho completo:
obter_cidade = attrgetter("cliente.endereco.cidade")
cidade = obter_cidade(pedido)Internamente, o getter percorre os atributos na ordem informada. Essa abordagem evita lambdas longas como lambda p: p.cliente.endereco.cidade. Ela é especialmente interessante em relatórios, tabelas, serializadores e pipelines de dados.
É importante entender que a notação pressupõe que todos os objetos intermediários existam. Se cliente for None, ocorrerá AttributeError. Quando os dados são opcionais, uma função normal com validação costuma ser mais adequada.
Obtendo vários atributos de uma vez
attrgetter aceita mais de um nome. Nesse caso, o retorno é uma tupla:
chave = attrgetter("nome", "preco")
print(chave(produtos[0]))
# ('Teclado', 180.0)Isso é útil para ordenação por múltiplos critérios. O Python compara tuplas item por item, então você pode ordenar primeiro por estoque e depois por nome:
ordenados = sorted(produtos, key=attrgetter("estoque", "nome"))A estabilidade da ordenação do Python garante comportamento previsível quando as chaves são iguais. Para entender melhor esse mecanismo, vale revisar o guia sobre sort e sorted no Python.
attrgetter com groupby
Outra combinação poderosa é com itertools.groupby. Antes de agrupar, os dados precisam estar ordenados pela mesma chave:
from itertools import groupby
from operator import attrgetter
produtos_ordenados = sorted(produtos, key=attrgetter("estoque"))
for estoque, grupo in groupby(produtos_ordenados, key=attrgetter("estoque")):
print(estoque, list(grupo))O detalhe da ordenação é indispensável, porque groupby agrupa elementos consecutivos, não todos os valores iguais espalhados pela sequência. O artigo sobre itertools no Python ajuda a compreender esse modelo de processamento preguiçoso.
Diferenças entre attrgetter, itemgetter e methodcaller
O módulo operator possui ferramentas relacionadas. attrgetter lê atributos de objetos. itemgetter lê itens por índice ou chave, sendo apropriado para listas, tuplas e dicionários. methodcaller cria uma função que chama um método.
from operator import itemgetter, methodcaller
itemgetter("nome")({"nome": "Ana"})
methodcaller("strip")(" texto ")Escolha a função de acordo com o contrato dos dados. Para um objeto com usuario.nome, use attrgetter. Para um dicionário com usuario["nome"], use itemgetter. Misturar os dois modelos costuma indicar que a estrutura de dados precisa ser melhor definida.
Quando uma lambda ainda é melhor
attrgetter é ideal quando a chave consiste somente em recuperar atributos. Quando há transformação, validação, valor padrão ou cálculo, uma lambda ou função nomeada é mais clara.
ordenados = sorted(produtos, key=lambda p: p.preco * (1 - p.desconto))Também prefira uma função normal quando precisar tratar atributos ausentes:
def cidade_segura(pedido):
cliente = getattr(pedido, "cliente", None)
endereco = getattr(cliente, "endereco", None)
return getattr(endereco, "cidade", "")
A função getattr aceita valor padrão, enquanto attrgetter não oferece essa opção. Para fundamentos de funções e composição, consulte o artigo sobre funções em Python.
Desempenho e reutilização
Em muitos cenários, attrgetter pode ser ligeiramente mais eficiente que uma lambda equivalente, pois é implementado em código otimizado da biblioteca padrão. No entanto, a diferença raramente deve ser o motivo principal da escolha. Legibilidade, intenção e manutenção são critérios mais importantes.
Quando o mesmo acesso aparece repetidamente, crie o getter uma vez:
por_data = attrgetter("criado_em")
registros = sorted(registros, key=por_data)
mais_recente = max(registros, key=por_data)Isso evita repetir strings e centraliza a regra. Em projetos maiores, getters reutilizáveis podem ficar perto dos modelos ou da camada de relatório.
Cuidados com propriedades e descriptors
attrgetter usa o acesso normal de atributos. Portanto, ele executa propriedades, descriptors e __getattr__. Se uma propriedade faz acesso ao banco, rede ou cálculo caro, ordenar uma lista pode disparar a operação muitas vezes. Não trate attrgetter como uma ferramenta de introspecção estática.
Quando for necessário inspecionar membros sem executar descriptors, a biblioteca inspect possui alternativas específicas. O guia sobre fluxo e retorno de funções também ajuda a estruturar funções de chave sem efeitos colaterais.
Boas práticas
Use nomes de atributos estáveis e evite construir caminhos a partir de entrada não confiável. Trate AttributeError na fronteira onde dados incompletos entram no sistema. Ordene antes de usar groupby. Prefira uma função nomeada quando a regra envolver mais do que leitura direta. Escreva testes para chaves compostas e caminhos aninhados.
Para detalhes oficiais, consulte a documentação de operator.attrgetter e a documentação de ordenação do Python.
Conclusão
operator.attrgetter é uma ferramenta pequena, mas muito útil para deixar ordenações, agrupamentos e transformações mais declarativos. Ele funciona com atributos simples, caminhos aninhados e múltiplos campos, integrando-se naturalmente a sorted, min, max e groupby. Use-o quando a operação for apenas recuperar atributos. Quando houver regras extras, valores padrão ou efeitos colaterais, uma função explícita continuará sendo a melhor escolha.







