
Cada
@property, classmethod, staticmethod e até mesmo a chamada de métodos comuns funcionam sob o capô com o mesmo mecanismo. Nos descritores. Um descritor é qualquer classe que implementa pelo menos um dos métodos do protocolo:
__get__, __set__ ou __delete__. Assim que você vincula uma instância dessa classe a um atributo de outra classe, o Python intercepta o controle de acesso.1️⃣ Hierarquia de prioridades
Quando você escreve
obj.attr, o Python não simplesmente acessa o dicionário. Ele segue uma cadeia estrita:▪️ Data Descriptor (definidos
__set__ ou __delete__). Tem prioridade absoluta. Se o nome do atributo é interceptado por um Data Descriptor, o Python ignora instance.__dict__. É por isso que você não pode sobrescrever um @property com uma atribuição comum (a menos que haja um setter).▪️ Dicionário da instância (
__dict__). Dados comuns do objeto.▪️ Non-Data Descriptor (definido apenas
__get__). Métodos comuns de classe são exatamente isso. Eles estão no fundo das prioridades. Se você colocar manualmente uma função com o nome do método no __dict__ do objeto, ela substituirá o método original. 2️⃣ Anti-padrão de estado compartilhado
O descritor é inicializado no nível da classe, não da instância. Ele é único para todos. Se você armazenar dados dentro da instância do descritor, sobrescreverá esse valor para todos os objetos da sua classe. Se o Vasya mudar a idade, o Petya envelhecerá junto com ele, porque ambas as instâncias acessam a mesma célula de memória do descritor.
Como escrever corretamente?
O estado deve ser armazenado estritamente no dicionário da própria instância. Para não escrever gambiarras, no Python 3.6+ foi introduzido o método mágico
__set_name__. Ele permite que o descritor saiba sob qual nome foi criado na classe, para armazenar dados legalmente no __dict__ do objeto específico.class Validator:
def __set_name__(self, owner, name):
# Lembra o nome do atributo para não hardcodar
self.private_name = f"_{name}"
def __get__(self, instance, owner):
if instance is None:
return self
return getattr(instance, self.private_name)
def __set__(self, instance, value):
# ✅ Escreve dados apenas no dicionário da INSTÂNCIA ESPECÍFICA
if not isinstance(value, int):
raise TypeError("Only integers allowed")
setattr(instance, self.private_name, value)
class User:
age = Validator() # age_validator.__set_name__ receberá name='age'Descritores são uma ferramenta poderosa para encapsular lógica. Toda a magia de frameworks como Django ou SQLAlchemy é construída sobre eles ☝️
#anatomia_do_python
Комментарии
0Комментариев пока нет.
Войдите, чтобы участвовать в обсуждении.