O Que Sao Descritores - Professor Jeronimo: O que são os descritores?
Professor Jeronimo: O que são os descritores?

O que são descritores e por que você provavelmente não precisa deles ainda

Descritores são mecanismos que permitem controlar como atributos de objetos são acessados, definidos ou removidos. No Python, eles representam um protocolo específico: qualquer objeto que implemente __get__, __set__ ou __delete__ pode funcionar como descritor. A maioria das pessoas os encontra sem perceber quando usa property, mas o protocolo vai muito além disso.

o que sao descritores na pratica

Na prática, um descritor é apenas um objeto que intercepta a leitura e escrita de atributos em outra classe. Quando você faz obj.atributo, o interpretador procura primeiro por um descritor na classe, depois pelo dicionário de instância. Essa ordem importa. Descritores não-data (só __get__) perdem para atributos normais da instância. Descritores data (__get__ + __set__) vencem sempre. Vou mostrar com código real. Isso é mais claro do que qualquer definição abstrata.

implementando um descritor simples

Aqui está um exemplo funcional de um descritor que valida tipos:

class TypedAttribute:
    def __init__(self, name, expected_type):
        self.name = name
        self.expected_type = expected_type

    def __get__(self, obj, objtype):
        if obj is None:
            return self
        return obj.__dict__.get(self.name)

    def __set__(self, obj, value):
        if not isinstance(value, self.expected_type):
            raise TypeError(f"{self.name} deve ser {self.expected_type.__name__}")
        obj.__dict__[self.name] = value

class Pessoa:
    nome = TypedAttribute("nome", str)
    idade = TypedAttribute("idade", int)

p = Pessoa()
p.nome = "João"  funciona
p.idade = 30     funciona
p.idade = "trinta"  levanta TypeError

Neste exemplo, o descritor armazena o valor real dentro de obj.__dict__ usando um nome que ele próprio controla. Isso evita loops infinitos de recursão — se você tentasse acessar self.valor dentro do descritor, chamaria __set__ novamente e travaria o programa.

👉 Clique no botão abaixo para saber mais sobre o assunto!

quando descritores aparecem no dia a dia

Todo mundo já usou descritores e não sabia. @property, classmethod, staticmethod — todos são descritores embutidos no Python. Funcionalidades de ORMs como SQLAlchemy e Django usam descritores massivamente para mapear colunas de banco para atributos de modelo. Frameworks de serialização também empregam o padrão. Se você já escreveu um decorator personalizado que se comporta diferente quando chamado como @decorator versus @decorator(args), basicamente recriou um descritor manual. O compilador não reconhece essa dualidade; é você quem precisa lidar com isso manualmente.

um problema real que eu encontrei

Num projeto de backend, precisei criar um sistema de cache distribuído onde múltiplos atributos de uma classe precisavam invalidar conjuntos de cache simultaneamente. A solução ingênua era criar uma propriedade para cada atributo com lógica de invalidação duplicada. O resultado foi uma classe com 40 linhas de boilerplate idêntico. A workaround que funcionou foi criar um descritor que recebe uma lista de chaves de cache no construtor. Quando o valor é atribuído, o descritor itera sobre as chaves e dispara invalidações. O código do modelo ficou com metade das linhas. O custo? Depuração. Quando algo dá errado, o traceback atravessa três frames de descritor antes de chegar ao seu código. Não é grave, mas conta tempo.

armadilhas que ninguém comenta

O primeiro erro comum é confundir a ordem de resolução de atributos. Se um descritor não-data existe na classe e a instância tem um atributo com o mesmo nome no __dict__, o Python retorna o da instância. Isso quebra expectativas. Teste sempre com vars(obj) para ver o que realmente está sendo armazenado. O segundo problema é mais sutil: descritores são compartilhados entre todas as instâncias da mesma classe. Cada instância vê o mesmo objeto descritor. Se você armazenar estado mutável dentro do descritor, todas as instâncias compartilham esse estado. Já vi isso causar bugs difíceis de rastrear em classes onde o desenvolvedor assumiu que cada instância tinha seu próprio dicionário interno no descritor. A solução é usar obj.__dict__ como storage, não variáveis de instância do próprio descritor.

alternativas e quando evitar

Para a maioria dos casos, property resolve. Se você precisa de validação simples em 3 ou 4 atributos, criar uma classe descritor completa é overengineering. Use properties normais. Descritores fazem sentido quando a lógica de acesso se repete em dezenas de atributos ou quando você precisa reutilizar o mesmo comportamento em múltiplas classes. Outra alternativa válida é usar __setattr__ e __getattr__ no nível da classe. São mais flexíveis mas muito mais caros em performance — cada acesso a atributo passa por métodos personalizados. Em estruturas de dados onde performance importa, descritores são significativamente mais rápidos que sobrescrever __setattr__.

resumo objetivo

Descritores são um protocolo Python que intercepta acesso a atributos via __get__ e __set__. Eles são a base de @property, @classmethod e @staticmethod. Servem para validar tipos, implementar lazy loading, gerenciar caches e outras tarefas meta-programáticas. Não os use quando uma property simples basta. Fique atento à ordem de resolução de atributos e ao fato de que o estado do descritor é compartilhado entre todas as instâncias. Isso resolve 90% dos problemas que aparecem na prática.