
Passo 1 de 8
Identificar a lógica de atributos que pode ser reutilizada
Reconheça quando uma regra de validação deixa de pertencer a um único atributo e pode ser concentrada em um descritor reutilizável.
Trilha de aprendizado · Nível 11 · Tutorial 9
Ao concluir, você poderá implementar um descritor reutilizável para controlar atributos de várias classes e explicar sua participação na resolução de atributos.
Identificar a lógica de atributos que pode ser reutilizada
Reconheça quando uma regra de validação deixa de pertencer a um único atributo e pode ser concentrada em um descritor reutilizável. 2 min
Controlar a leitura com __get__
Implemente a leitura controlada de um atributo e diferencie o acesso feito por uma instância daquele feito pela classe. 3 min
Validar atribuições sem compartilhar os valores
Implemente __set__ para validar textos antes de gravá-los e mantenha cada valor no dicionário da respectiva instância. 4 min
Associar nomes automaticamente com __set_name__
Use __set_name__ para derivar a chave interna de cada atributo e reutilizar um descritor em campos e classes diferentes. 3 min
Controlar a remoção e classificar os descritores
Defina uma política explícita para del e identifique descritores de dados e de não dados pelos métodos do seu tipo. 3 min
Prever qual valor vence na leitura
Entenda a ordem usual de leitura de atributos por instância e preveja conflitos entre descritores e valores no dicionário do objeto. 4 min
Relacionar descritores a property e métodos
Reconheça descritores em recursos conhecidos e escolha entre uma property e um descritor próprio conforme o ganho de reutilização. 2 min
Aplicação final: reutilizar e verificar o descritor
Integre um descritor reutilizável, execute verificações locais e justifique quando ele oferece vantagem sobre properties específicas. 4 min

Passo 1 de 8
Reconheça quando uma regra de validação deixa de pertencer a um único atributo e pode ser concentrada em um descritor reutilizável.
Imagine que Produto.nome, Produto.marca e Categoria.titulo precisem aceitar apenas textos com um comprimento mínimo — mas cada campo pode exigir um mínimo diferente.
Copiar essa regra em vários setters torna a manutenção repetitiva. Um descritor permite concentrar a política em um objeto reutilizável, configurado para cada atributo.
A regra reutilizável fica associada às classes que declaram os atributos; os objetos consumidores usam essa regra ao acessar seus campos.

Cada atributo declarado recebe um objeto descritor; a classe do descritor pode ser reutilizada em campos e classes diferentes.
Veja uma declaração ainda incompleta — a implementação do protocolo virá nos próximos passos:
TextoMinimo é a classe do descritor: ela define a regra reutilizável.TextoMinimo(3) cria um objeto descritor, configurado com mínimo 3.Produto é a classe consumidora: ela declara o atributo controlado, nome.Exemplo estrutural: o comportamento de TextoMinimo será implementado nos próximos passos.
class TextoMinimo:
def __init__(self, minimo):
self.minimo = minimo
class Produto:
nome = TextoMinimo(3)
marca = TextoMinimo(2)
class Categoria:
titulo = TextoMinimo(5)Dica
O descritor é associado no corpo da classe consumidora, como nome = TextoMinimo(3). Não basta colocá-lo no __dict__ de uma instância específica: assim a regra não participa do acesso ao atributo declarado pela classe.
No código apresentado, qual alternativa identifica corretamente os papéis de nome = TextoMinimo(3) dentro de Produto?

Passo 2 de 8
Implemente a leitura controlada de um atributo e diferencie o acesso feito por uma instância daquele feito pela classe.
Quando um atributo controlado por descritor é lido, Python chama __get__(self, instance, owner).
self: o próprio objeto descritor.instance: a instância acessada, como produto, ou None no acesso pela classe.owner: a classe envolvida no acesso, como Produto.Assim, produto.nome e Produto.nome podem seguir caminhos diferentes, mesmo apontando para o mesmo atributo declarado na classe.
O valor público nome pode ser lido de uma chave interna, como _nome, sem expor essa convenção na interface da classe.

Pela instância, a leitura busca o valor interno. Pela classe, instance é None e o descritor pode ser devolvido.
Em instâncias comuns, instance.__dict__ é um dicionário que pode guardar atributos. Aqui, o descritor lê somente a chave interna _nome.
Não é correto supor que todo objeto Python tenha __dict__: classes com certas configurações podem não oferecê-lo. Neste exemplo didático, o descritor foi escolhido para objetos comuns que o possuem.
Se a chave interna não existir, converta o KeyError do dicionário em AttributeError. Para quem usa o atributo, o resultado é coerente: o atributo ainda não está disponível.
Teste este código em um arquivo Python. A chave interna é configurada manualmente por enquanto; a associação automática de nomes será tratada depois.
class LeitorDeTexto:
def __init__(self, chave_interna):
self.chave_interna = chave_interna
def __get__(self, instance, owner):
if instance is None:
return self
try:
return instance.__dict__[self.chave_interna]
except KeyError:
raise AttributeError(
f"{owner.__name__} ainda não tem {self.chave_interna!r}"
) from None
class Produto:
nome = LeitorDeTexto("_nome")
produto = Produto()
produto.__dict__["_nome"] = "Caderno"
print(produto.nome) # Caderno
print(Produto.nome) # objeto LeitorDeTexto
vazio = Produto()
try:
print(vazio.nome)
except AttributeError as erro:
print(erro) # Produto ainda não tem '_nome'Considere a classe Produto e o descritor LeitorDeTexto do exemplo. Relacione cada situação ao comportamento de __get__.
Toque em um item e depois no par correspondente.

Passo 3 de 8
Implemente set para validar textos antes de gravá-los e mantenha cada valor no dicionário da respectiva instância.
Quando você executa objeto.nome = valor, o método __set__(self, instance, value) do descritor controla essa atribuição.
No nosso contrato, TextoMinimo aceita somente valores do tipo str com o comprimento mínimo configurado. Não converta valores implicitamente: um número como 123 deve causar TypeError, e uma string curta demais deve causar ValueError.
A ordem importa: valide tudo antes de gravar. Assim, se a atribuição falhar, o valor que a instância já possuía permanece intacto.
O descritor é compartilhado pela classe, mas cada objeto consumidor guarda seu próprio valor na chave interna.

À esquerda, guardar value no descritor faz uma instância sobrescrever a outra. À direita, a configuração fica no descritor e cada valor fica em instance.__dict__.
Execute este exemplo em um arquivo Python no seu computador.
class TextoMinimo:
def __init__(self, minimo):
self.minimo = minimo
self.chave_interna = "_nome"
def __get__(self, instance, owner):
if instance is None:
return self
try:
return instance.__dict__[self.chave_interna]
except KeyError:
raise AttributeError("nome ainda não foi definido") from None
def __set__(self, instance, value):
if not isinstance(value, str):
raise TypeError("nome deve ser uma string")
if len(value) < self.minimo:
raise ValueError(
f"nome deve ter ao menos {self.minimo} caracteres"
)
instance.__dict__[self.chave_interna] = value
class Pessoa:
nome = TextoMinimo(3)
ana = Pessoa()
bruno = Pessoa()
ana.nome = "Ana"
bruno.nome = "Bruno"
print(ana.nome) # Ana
print(bruno.nome) # Bruno
print(ana.__dict__) # {'_nome': 'Ana'}
print(bruno.__dict__) # {'_nome': 'Bruno'}
try:
ana.nome = "Li"
except ValueError as erro:
print(erro)
print(ana.nome) # Ana: o valor anterior foi preservado
try:
bruno.nome = 42
except TypeError as erro:
print(erro)
print(bruno.nome) # Bruno: o valor anterior foi preservadoAtenção
Dentro de __set__, não escreva instance.nome = value. Essa linha acionaria o mesmo __set__ novamente, causando recursão até um erro.
Grave diretamente na chave interna: instance.__dict__[self.chave_interna] = value. Neste exemplo, "_nome" ainda é fixa; no próximo passo ela será derivada automaticamente do atributo associado.
Execute o código apresentado. Em sua resposta, informe os valores finais de ana.nome e bruno.nome, indique onde cada valor aparece e explique o que ocorreu após as duas atribuições inválidas.
Escreva pelo menos 80 caracteres (0/80).

Passo 4 de 8
Use set_name para derivar a chave interna de cada atributo e reutilizar um descritor em campos e classes diferentes.
Até aqui, o descritor já guarda sua configuração, como o comprimento mínimo, e os valores ficam em cada instância. Falta descobrir automaticamente qual atributo público ele controla.
Quando Python cria uma classe, ele chama __set_name__(self, owner, name) para cada objeto associado no corpo da classe cujo tipo define esse método:
self: o objeto descritor;owner: a classe que o declarou;name: o nome público usado na declaração, como nome.Assim, minimo vem da construção de TextoMinimo(3), enquanto name vem da associação nome = ... no corpo da classe.
A imagem separa a configuração criada antes da classe da associação que ocorre durante sua criação.

A construção define a regra; a associação à classe fornece o nome do atributo.
A chave interna e as mensagens usam o nome recebido em __set_name__. Cada declaração abaixo cria um novo objeto TextoMinimo, ainda que todas usem a mesma implementação.
class TextoMinimo:
def __init__(self, minimo):
self.minimo = minimo
def __set_name__(self, owner, name):
self.nome_publico = name
self.chave_interna = f"_{name}"
def __get__(self, instance, owner):
if instance is None:
return self
try:
return instance.__dict__[self.chave_interna]
except KeyError:
raise AttributeError(
f"{self.nome_publico!r} ainda não foi definido"
) from None
def __set__(self, instance, value):
if not isinstance(value, str):
raise TypeError(f"{self.nome_publico!r} deve ser texto")
if len(value) < self.minimo:
raise ValueError(
f"{self.nome_publico!r} deve ter ao menos {self.minimo} caracteres"
)
instance.__dict__[self.chave_interna] = value
class Produto:
nome = TextoMinimo(3)
marca = TextoMinimo(2)
class Categoria:
titulo = TextoMinimo(4)
produto = Produto()
produto.nome = "Caderno"
produto.marca = "AC"
categoria = Categoria()
categoria.titulo = "Papelaria"
print(produto.__dict__) # {'_nome': 'Caderno', '_marca': 'AC'}
print(categoria.__dict__) # {'_titulo': 'Papelaria'}Dica
Produto.nome, Produto.marca e Categoria.titulo usam a mesma classe de descritor, mas cada expressão TextoMinimo(...) cria um objeto descritor diferente. Isso permite que cada um guarde sua própria associação (nome_publico e chave_interna).
Não faça isto quando o descritor guarda apenas um nome e uma chave interna:
texto_curto = TextoMinimo(2)
class Produto:
nome = texto_curto
marca = texto_curto__set_name__ será chamado duas vezes no mesmo objeto. A segunda chamada pode substituir os dados da primeira: ao final, o descritor pode se considerar associado somente a marca. Para este design, crie um objeto descritor por atributo declarado.
Atenção
A chamada automática ocorre durante a criação da classe. Portanto, isto não chama __set_name__ automaticamente:
class Produto:
pass
Produto.nome = TextoMinimo(3)Nesse caso, o descritor ainda não recebeu nome_publico nem chave_interna. Para este padrão, declare o atributo no corpo da classe. Tratamentos mais gerais para associações dinâmicas ficam fora deste tutorial.
No código do exemplo, a gravação em produto.marca usa a chave interna __ em `produto.dict__`.
Qual declaração reutiliza a regra sem arriscar sobrescrever a associação de nome no mesmo objeto descritor?

Passo 5 de 8
Defina uma política explícita para del e identifique descritores de dados e de não dados pelos métodos do seu tipo.
Além de controlar leitura e atribuição, um descritor pode definir o que ocorre em del objeto.atributo por meio de __delete__(self, instance).
No TextoMinimo, a política será remover a chave interna criada em __set_name__. Se não houver valor armazenado, a remoção sinaliza AttributeError, assim como a leitura de um valor indisponível.

A remoção passa pelo descritor e atua na chave interna da instância.
Acrescente este método à classe TextoMinimo, que já possui self.chave_interna definida em __set_name__.
def __delete__(self, instance):
try:
del instance.__dict__[self.chave_interna]
except KeyError:
raise AttributeError(
f"{self.nome_publico!r} não possui valor"
) from NoneCom uma classe Produto que use nome = TextoMinimo(3), execute estas operações na ordem mostrada.
produto = Produto()
produto.nome = "Caderno"
print(produto.nome) # Caderno
del produto.nome
print(produto.__dict__) # {}
print(produto.nome) # AttributeError: 'nome' não possui valorAtenção
Se o tipo do descritor define __set__, ele já controla atribuições. Apenas deixar de implementar __delete__ não faz com que del objeto.atributo remova automaticamente um valor do armazenamento interno: a operação é rejeitada com AttributeError.
Implemente __delete__ somente quando quiser oferecer uma política de remoção.
Depois de del produto.nome remover a chave interna, uma leitura de produto.nome deve resultar em AttributeError.
A categoria é determinada pelos métodos definidos no tipo do descritor:
__set__ ou __delete__ (e pode também definir __get__).__get__, mas não define __set__ nem __delete__.Portanto, __set_name__ recebe o nome da associação, mas não participa dessa classificação. Um método que rejeita uma operação ainda conta: se __set__ existe e sempre levanta erro, o descritor continua sendo de dados.

set_name ajuda a configurar o descritor, mas não decide sua categoria.
Considere apenas os métodos declarados em cada classe de descritor.
class Leitura:
def __get__(self, instance, owner):
return 42
class Gravavel:
def __get__(self, instance, owner):
return instance.__dict__["_valor"]
def __set__(self, instance, value):
instance.__dict__["_valor"] = value
class Removivel:
def __delete__(self, instance):
del instance.__dict__["_valor"]
def __set_name__(self, owner, name):
self.nome = nameQual classificação está correta para os três tipos do código?

Passo 6 de 8
Entenda a ordem usual de leitura de atributos por instância e preveja conflitos entre descritores e valores no dicionário do objeto.
No acesso usual por instância, como em obj.nome, Python localiza o candidato na classe ou em suas classes-base e aplica uma ordem de precedência. Sem personalizar a busca de atributos, a leitura segue esta sequência:
__get__;"nome" em obj.__dict__;__get__;Um descritor de dados define __set__ ou __delete__; por isso, ele vence uma chave pública de mesmo nome no dicionário da instância.
A imagem representa a disputa pelo nome público nome. A chave interna usada pelo descritor, como _nome, é outro nome e não participa desse conflito direto.

A busca considera a chave pública em conflito. Uma chave interna, como _nome, é independente.
Execute este código no seu computador e compare os valores impressos.
class Dado:
def __get__(self, instance, owner):
if instance is None:
return self
return "valor do descritor de dados"
def __set__(self, instance, value):
instance.__dict__["_protegido"] = value
class NaoDado:
def __get__(self, instance, owner):
if instance is None:
return self
return "valor do descritor de não dados"
class Registro:
protegido = Dado()
aberto = NaoDado()
registro = Registro()
registro.__dict__["protegido"] = "tentativa de encobrir"
registro.__dict__["aberto"] = "valor da instância"
print(registro.protegido)
print(registro.aberto)
del registro.__dict__["aberto"]
print(registro.aberto)Exemplo
A saída é:
valor do descritor de dadosvalor da instânciavalor do descritor de não dados
A chave pública "protegido" existe em registro.__dict__, mas não encobre protegido, pois esse é um descritor de dados. Já "aberto" encobre o descritor de não dados enquanto existir. Após o del, o descritor volta a ser visível.
Dica
No descritor TextoMinimo, o valor validado deve ficar em uma chave interna, como _nome. Isso evita recursão e também evita confundir esse armazenamento com uma chave pública conflitante, como nome.
Coloque as fontes na ordem usual em que podem fornecer o valor de obj.nome, da maior para a menor precedência.
Atenção
Escrever diretamente em obj.__dict__["_nome"] pode inserir um valor que nunca passou por __set__. Um descritor organiza e reutiliza regras de acesso; ele não impede código que tenha acesso ao objeto de alterar seu armazenamento interno.
No exemplo anterior, por que registro.__dict__["protegido"] não vence a leitura de registro.protegido, enquanto registro.__dict__["aberto"] vence temporariamente a leitura de registro.aberto?
Escreva pelo menos 80 caracteres (0/80).

Passo 7 de 8
Reconheça descritores em recursos conhecidos e escolha entre uma property e um descritor próprio conforme o ganho de reutilização.
Você já usou property e métodos de instância. Ambos são objetos colocados no corpo da classe e participam do acesso a atributos por meio de descritores.
Uma property é um descritor de dados, inclusive se você não declarou um setter. Por isso, uma entrada com o mesmo nome público em obj.__dict__ não a encobre na leitura.
Já uma função definida no corpo da classe é um descritor de não dados: seu __get__ participa da criação do método vinculado quando o acesso ocorre pela instância.
Compare a precedência aplicada a uma property e a uma função de classe.

Property vence uma chave pública da instância; uma função pode ser encoberta por essa chave.
Exemplo
class Conta:
@property
def saldo(self):
return 100
def resumo(self):
return "Conta ativa"
conta = Conta()
conta.__dict__["saldo"] = 999
conta.__dict__["resumo"] = "valor armazenado"
print(conta.saldo) # 100
print(conta.resumo) # 'valor armazenado'A chave "saldo" não encobre a property, pois ela é um descritor de dados. A chave "resumo" encobre a função encontrada na classe, pois funções são descritores de não dados.
Essa é uma demonstração da resolução usual de atributos; escrever diretamente em __dict__ contorna interfaces e não é uma prática recomendada para o uso normal do objeto.
Dica
Uma property sem setter ainda é descritor de dados. A tentativa normal de atribuir ao atributo é rejeitada; a ausência de um setter público não a transforma em descritor de não dados.
Prefira uma property quando a regra pertence a um atributo específico de uma classe: ela deixa a intenção local e direta.
Crie um descritor próprio quando a mesma política de leitura, validação e armazenamento precisar ser aplicada de modo consistente em vários campos ou em várias classes — como TextoMinimo associado a nomes e descrições com configurações diferentes.
O descritor reduz repetição, mas cobra um custo: configuração, chave interna, entendimento de __set_name__ e conhecimento da resolução de atributos. Reutilização pequena não costuma compensar essa complexidade.
A estrutura deve acompanhar a quantidade de lugares que compartilham a regra.

Regra local: property. Política repetida: descritor próprio.
A classe Pedido possui total, calculado a partir dos itens. Nenhuma outra classe precisa desse cálculo e não há outros campos com a mesma política. Qual é a escolha mais apropriada?
Uma property definida apenas com getter é classificada como:

Passo 8 de 8
Integre um descritor reutilizável, execute verificações locais e justifique quando ele oferece vantagem sobre properties específicas.
O descritor guarda apenas sua configuração e os nomes associados. Cada valor concreto fica no __dict__ da instância consumidora, sob uma chave interna. Assim, a mesma classe TextoMinimo controla campos de Produto e Categoria sem misturar valores.
Observe que cada declaração cria um objeto descritor próprio, enquanto cada objeto consumidor mantém seus próprios valores internos.

O descritor contém regra e associação; as instâncias contêm os valores.
Crie um arquivo, por exemplo verificar_descritor.py, cole o script inteiro e execute python verificar_descritor.py.
class TextoMinimo:
def __init__(self, minimo):
self.minimo = minimo
def __set_name__(self, owner, name):
self.nome_publico = name
self.chave_interna = f"_{name}"
def __get__(self, instance, owner):
if instance is None:
return self
try:
return instance.__dict__[self.chave_interna]
except KeyError:
raise AttributeError(
f"{self.nome_publico!r} ainda não foi definido"
) from None
def __set__(self, instance, value):
if not isinstance(value, str):
raise TypeError(f"{self.nome_publico} deve ser str")
if len(value) < self.minimo:
raise ValueError(
f"{self.nome_publico} deve ter ao menos {self.minimo} caracteres"
)
instance.__dict__[self.chave_interna] = value
def __delete__(self, instance):
try:
del instance.__dict__[self.chave_interna]
except KeyError:
raise AttributeError(
f"{self.nome_publico!r} ainda não foi definido"
) from None
class Produto:
nome = TextoMinimo(3)
descricao = TextoMinimo(10)
class Categoria:
titulo = TextoMinimo(4)
p1 = Produto()
p2 = Produto()
categoria = Categoria()
p1.nome = "Teclado"
p1.descricao = "Teclado sem fio"
p2.nome = "Mouse"
p2.descricao = "Mouse ergonomico"
categoria.titulo = "Casa"
# Cada instância e cada campo conservam valores independentes.
assert p1.nome == "Teclado"
assert p2.nome == "Mouse"
assert p1.descricao == "Teclado sem fio"
assert categoria.titulo == "Casa"
# Acesso pela classe devolve o objeto descritor escolhido como interface.
assert isinstance(Produto.nome, TextoMinimo)
assert Produto.nome.nome_publico == "nome"
assert Produto.descricao.chave_interna == "_descricao"
# Uma falha de validação não altera o valor que já era válido.
try:
p1.nome = "ab"
except ValueError:
pass
else:
raise AssertionError("Era esperado ValueError")
assert p1.nome == "Teclado"
try:
categoria.titulo = 42
except TypeError:
pass
else:
raise AssertionError("Era esperado TypeError")
assert categoria.titulo == "Casa"
# O descritor de dados vence uma chave pública conflitante na instância.
p1.__dict__["nome"] = "Valor falso"
assert p1.nome == "Teclado"
# Após del, a leitura segue o contrato de atributo indisponível.
del p2.nome
try:
p2.nome
except AttributeError:
pass
else:
raise AssertionError("Era esperado AttributeError")
print("Todas as verificações passaram.")Se a execução terminar com Todas as verificações passaram., o script comprovou: associação automática de nomes, isolamento entre instâncias e campos, validação antes da gravação, acesso pela classe, precedência do descritor de dados e indisponibilidade após remoção.
Agora acrescente, no corpo de Produto, a linha marca = TextoMinimo(3). Depois de criar p1, atribua p1.marca = "ABC" e verifique com assert p1.marca == "ABC". Tente também p1.marca = "X", capture ValueError e confirme que p1.marca ainda é "ABC".
Dica
marca recebe outro objeto TextoMinimo, com sua própria associação e configuração. Você reutiliza a política sem copiar getters, setters e mensagens para cada campo.
Quais resultados da execução comprovam isolamento, preservação do estado após erro e precedência? Por que o campo marca pode reutilizar a implementação sem compartilhar seu valor com nome?
Escreva pelo menos 120 caracteres (0/120).
Resumo
Use o protocolo para concentrar uma política repetida de atributos, mantendo a responsabilidade pelo estado nos objetos que consomem essa política.
Em que situação você escolheria TextoMinimo em vez de uma property específica? Cite o ganho de reutilização e um custo da escolha.
Escreva pelo menos 90 caracteres (0/90).
Parabéns! Você concluiu: Reutilizar controle de atributos com descritores
Milhares de cursos online em vídeo, ebooks e áudiobooks.
Para testar seus conhecimentos no decorrer dos cursos online
Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail
Baixe nosso aplicativo pelo QR Code ou pelos links abaixo:.
+ de 10 milhões
de alunos
Certificado grátis e
válido em todo o Brasil
60 mil exercícios
gratuitos
4,8/5 classificação
nas lojas de apps
Cursos gratuitos em
vídeo, ebooks e audiobooks