operator.attrgetter: ordene objetos por atributos

Publicado em: 02/09/2026
Tempo de leitura: 5 minutos
Desenvolvedor organizando dados com operator.attrgetter no Python

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.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Código assíncrono representando asyncio.eager_task_factory no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    asyncio.eager_task_factory: reduza overhead de tarefas

    Aprenda asyncio.eager_task_factory no Python para reduzir overhead, entender mudanças de ordem e otimizar corrotinas curtas com segurança.

    Ler mais

    Tempo de leitura: 4 minutos
    14/09/2026
    Desenvolvedor trabalhando com timestamps UTC e calendar.timegm no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    calendar.timegm: converta UTC para timestamp Unix

    Aprenda calendar.timegm no Python para converter datas UTC em timestamps Unix com segurança, testes e integração com datetime.

    Ler mais

    Tempo de leitura: 6 minutos
    14/09/2026
    Programador analisando código para identificar tipos MIME de arquivos no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    mimetypes.guess_file_type: detecte tipos MIME

    Aprenda mimetypes.guess_file_type no Python para detectar tipos MIME em caminhos, URLs, uploads e respostas HTTP com fallbacks seguros.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Componentes de servidor representando interpretadores Python executando em paralelo
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    InterpreterPoolExecutor: paralelismo real no Python

    Aprenda InterpreterPoolExecutor no Python para executar tarefas CPU-bound em interpretadores isolados com paralelismo real.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Microprocessador representando CPUs disponíveis para um processo Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    os.process_cpu_count: conte CPUs disponíveis

    Aprenda os.process_cpu_count no Python para dimensionar workers conforme as CPUs realmente disponíveis ao processo.

    Ler mais

    Tempo de leitura: 4 minutos
    12/09/2026
    Laptop com código digital representando dados BLOB no SQLite
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    sqlite3.Blob: leia BLOBs sem carregar tudo na memória

    Aprenda sqlite3.Blob no Python para ler e gravar BLOBs em partes, reduzir memória e trabalhar com dados binários no SQLite.

    Ler mais

    Tempo de leitura: 5 minutos
    12/09/2026