Módulo 11 - Metaclasses e descritores
Interceptar atributos: __getattr__ e amigos
10 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Diferenciar __getattr__ (só o que falta) de __getattribute__ (todo acesso).
- Usar __setattr__ para validar ou controlar a escrita de atributos.
- Evitar o erro clássico de recursão infinita nesses métodos.
- Reconhecer casos legítimos como proxies e objetos dinâmicos.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: Interceptar atributos: __getattr__ e amigos.
Os objetivos desta aula. Diferenciar __getattr__ (só o que falta) de __getattribute__ (todo acesso). Usar __setattr__ para validar ou controlar a escrita de atributos. Evitar o erro clássico de recursão infinita nesses métodos. Reconhecer casos legítimos como proxies e objetos dinâmicos.
Veja o essencial, parte por parte.
__getattr__ contra __getattribute__. __getattr__ roda só quando o atributo não existe pelo caminho normal.
__setattr__ e a armadilha da recursão. Dentro de __setattr__, nunca faça self.nome = valor: isso chama __setattr__ de novo.
Quando esses ganchos valem a pena. Como quase tudo neste módulo, esses ganchos são poderosos e pedem parcimônia.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
__getattr__ contra __getattribute__
Os descritores da aula anterior ficam na classe e controlam um atributo específico. Estes ganchos ficam no objeto e controlam o acesso a atributos em geral. O par mais importante é __getattr__ e __getattribute__, e a diferença entre eles é sutil e decisiva. O __getattribute__ é chamado em toda leitura de atributo, sem exceção. Já o __getattr__ é o plano B: ele só é chamado quando a busca normal falha, isto é, quando o atributo não foi encontrado. Na prática, você quase sempre quer __getattr__, porque ele não atrapalha os atributos que já existem.
class Config:
def __init__(self):
self.tema = "claro"
def __getattr__(self, nome):
# So chamado quando o atributo nao existe de verdade.
return f"[opcao {nome} nao definida]"
c = Config()
print(c.tema) # claro (existe: __getattr__ nem e chamado)
print(c.idioma) # [opcao idioma nao definida] (nao existe: cai no __getattr__)__getattr__ atua só no que falta; tema existe e passa direto, idioma cai no gancho.
O exemplo deixa a diferença palpável. Como tema existe no objeto, lê-lo não aciona __getattr__. Já idioma não existe, então a busca normal falha e o Python chama __getattr__, que devolve um valor padrão amigável em vez de levantar AttributeError. Esse padrão é a base de objetos de configuração flexíveis e de proxies que repassam chamadas para outro objeto. Se você usasse __getattribute__ aqui, teria que tratar todo e qualquer acesso, inclusive o de tema, e o risco de erro cresceria muito.
__setattr__ e a armadilha da recursão
Para o lado da escrita existe o __setattr__, chamado toda vez que você faz objeto.atributo = valor. Ele é útil para validar antes de gravar, tornar um objeto imutável ou registrar mudanças. Mas aqui mora a armadilha mais famosa desses ganchos: a recursão infinita. Dentro de __setattr__, se você escrever self.nome = valor para guardar o dado, isso chama __setattr__ de novo, que chama de novo, sem fim. A saída correta é gravar direto no dicionário do objeto, com super().__setattr__ ou self.__dict__.
class Registro:
def __setattr__(self, nome, valor):
if not nome.isidentifier():
raise ValueError(f"nome de campo invalido: {nome}")
print(f"gravando {nome} = {valor!r}")
# Certo: grava sem chamar __setattr__ de novo.
super().__setattr__(nome, valor)
# ERRADO seria: self.__dict__[nome] direto tambem serve,
# mas self.nome = valor causaria recursao infinita.
r = Registro()
r.saldo = 100 # gravando saldo = 100
print(r.saldo) # 100Em __setattr__, use super().__setattr__ para gravar sem recursão infinita.
Quando esses ganchos valem a pena
Como quase tudo neste módulo, esses ganchos são poderosos e pedem parcimônia. Os casos legítimos são bem definidos. Um proxy, objeto que repassa chamadas para outro, usa __getattr__ para delegar tudo que não conhece. Um objeto de configuração ou um wrapper sobre dados semiestruturados usa __getattr__ para expor chaves como atributos. Um objeto imutável usa __setattr__ para recusar mudanças depois de construído. Todos esses são padrões reconhecíveis, com uma razão clara.
O que você deve evitar é usar esses ganchos para economizar algumas linhas em código comum. Um objeto que intercepta todo acesso a atributo é difícil de depurar, confunde ferramentas de autocompletar e esconde erros de digitação, já que um atributo escrito errado pode simplesmente cair no __getattr__ em vez de estourar. Sempre que um descritor, uma property ou um atributo comum resolverem, prefira-os. Reserve __getattr__ para delegação e objetos genuinamente dinâmicos, e __getattribute__ quase nunca, pois raríssimo é o problema que só ele resolve.
Teste rápido
Qual a diferença central entre __getattr__ e __getattribute__?
Perguntas frequentes
- Por que __getattr__ é preferível a __getattribute__ na maioria dos casos?
- Porque __getattr__ só age quando o atributo não existe, deixando o acesso normal intacto e rápido. __getattribute__ intercepta tudo, exige tratar cada acesso e é fácil de quebrar com recursão. Para valores dinâmicos e proxies, __getattr__ dá o mesmo resultado com muito menos risco.
- O que causa o RecursionError nesses métodos?
- Acessar o próprio atributo pelo caminho comum dentro do gancho. Em __setattr__, self.nome = valor chama __setattr__ de novo; em __getattribute__, self.x chama __getattribute__ de novo. A solução é usar super().__setattr__, super().__getattribute__ ou self.__dict__ para acessar sem reentrar no gancho.
- Descritor ou __getattr__, quando usar cada um?
- Use um descritor quando quer controlar um atributo específico e talvez reaproveitar a regra entre classes. Use __getattr__ quando quer um comportamento genérico para atributos que não existem, como delegar para outro objeto. Descritor é cirúrgico; __getattr__ é abrangente.
- Esses ganchos funcionam com métodos também?
- Sim, porque métodos são apenas atributos que resultam em objetos chamáveis. Um proxy com __getattr__, por exemplo, pode repassar chamadas de método para o objeto interno. O acesso ao método passa pelo mesmo mecanismo de busca de atributo que os dados.
- Usar __getattr__ atrapalha o autocompletar do editor?
- Pode atrapalhar, sim. Como os atributos são resolvidos em tempo de execução, o editor não sabe de antemão quais existem e o autocompletar fica cego. É mais um motivo para reservar esses ganchos a casos que realmente pedem dinamismo, e não para código comum.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.