Descritores: __get__, __set__, __delete__
Implemente o protocolo de descritores para controlar o acesso a atributos.
Descritores: __get__, __set__, __delete__ é uma aula grátis de Python Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Python Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Python Academy inclui 4 aulas no total.
O que é um descritor?
Um descritor é um objeto que define __get__, __set__ ou __delete__ e é atribuído como um atributo de classe. O Python chama esses métodos durante o acesso ao atributo.
class Descriptor:
def __get__(self, obj, objtype=None):
print(f"__get__ called, obj={obj}")
return 42
class MyClass:
attr = Descriptor()
print(MyClass().attr) # __get__ called ... 42Descritores de dados versus descritores sem dados
Descritores de dados definem __set__ ou __delete__ e têm precedência sobre o __dict__ da instância. Descritores sem dados (somente __get__) cedem lugar ao __dict__.
class DataDesc:
def __get__(self, obj, t): return "data"
def __set__(self, obj, val): pass
class NonDataDesc:
def __get__(self, obj, t): return "nondata"
class C:
d = DataDesc()
n = NonDataDesc()
c = C()
c.__dict__["n"] = "instance" # shadows the non-data desc
c.__dict__["d"] = "instance" # does NOT shadow data desc
print(c.n) # instance
print(c.d) # dataAssinatura de __get__
__get__(self, obj, objtype): obj é a instância (ou None quando o acesso é feito na classe); objtype é a classe.
class Verbose:
def __get__(self, obj, objtype=None):
if obj is None:
return self # accessed on class
return f"value for {obj!r}"
class Widget:
label = Verbose()
print(Widget.label) # <Verbose object>
print(Widget().label) # value for <Widget object>__set__ e validação
Implemente __set__(self, obj, value) para validar ou transformar um valor antes de armazená-lo.
class PositiveInt:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, t):
return obj.__dict__.get(self.name)
def __set__(self, obj, val):
if not isinstance(val, int) or val <= 0:
raise ValueError(f"{self.name} must be a positive int")
obj.__dict__[self.name] = val
class Rect:
width = PositiveInt()
height = PositiveInt()
r = Rect()
r.width = 10
# r.width = -1 # ValueError__set_name__
O Python 3.6 ou posterior chama __set_name__(owner, name) no descritor quando ele é atribuído a uma classe, dando-lhe acesso ao próprio nome do atributo.
class Typed:
def __set_name__(self, owner, name):
self.public = name
self.private = "_" + name
def __get__(self, obj, t):
return None if obj is None else getattr(obj, self.private, None)
def __set__(self, obj, val):
setattr(obj, self.private, val)
class User:
name = Typed()
u = User()
u.name = "Alice"
print(u.name) # Alice__delete__ para remoção de atributos
Defina __delete__(self, obj) para interceptar del obj.attr.
class Protected:
def __set_name__(self, owner, name): self.name = name
def __get__(self, obj, t):
return obj.__dict__.get(self.name)
def __set__(self, obj, v): obj.__dict__[self.name] = v
def __delete__(self, obj):
raise AttributeError(f"Cannot delete {self.name}")
class Config:
host = Protected()
c = Config(); c.host = "localhost"
# del c.host # AttributeErrorFunções são descritores sem dados
As funções implementam __get__ para retornar um método vinculado quando acessadas em uma instância. É assim que funciona a vinculação de métodos do Python.
def greet(self):
return f"Hello from {self}"
class C: pass
C.greet = greet
c = C()
print(c.greet()) # Hello from <C object>classmethod e staticmethod são descritores
classmethod e staticmethod são classes de descritores integradas que envolvem funções e alteram o valor retornado por __get__.
class MyClass:
@classmethod
def from_string(cls, s):
return cls()
# classmethod.__get__ returns a bound method with cls
# staticmethod.__get__ returns the plain functionDescritor de atributos sob demanda
Crie um descritor que calcule um valor uma vez e o armazene em cache por instância.
class LazyAttr:
def __init__(self, func): self.func = func
def __set_name__(self, owner, name): self.name = name
def __get__(self, obj, t):
if obj is None: return self
val = self.func(obj)
obj.__dict__[self.name] = val # shadows descriptor
return val
class Report:
@LazyAttr
def summary(self):
print("computing...")
return "done"
r = Report()
print(r.summary) # computing... done
print(r.summary) # done (cached)property é um descritor
property é um descritor de dados implementado em C. Entender descritores ajuda a compreender como @property, @classmethod e @staticmethod funcionam.
class Circle:
def __init__(self, r): self._r = r
@property
def radius(self): return self._r
@radius.setter
def radius(self, v):
if v < 0: raise ValueError
self._r = v
# property is: property.__get__ = fget, property.__set__ = fsetOrdem de consulta de descritores
Ordem de consulta de atributos do Python: 1) descritores de dados do tipo (MRO), 2) __dict__ da instância, 3) descritores sem dados e variáveis de classe.
# Pseudocode for obj.attr:
# 1. Check type(obj).__mro__ for a data descriptor
# 2. Check obj.__dict__
# 3. Check type(obj).__mro__ for non-data descriptor or class var
# 4. Raise AttributeErrorVerificação rápida
Que tipo de descritor tem precedência sobre o __dict__ da instância?
Recapitulação
Os descritores interceptam o acesso a atributos por meio de __get__, __set__ e __delete__. Os descritores de dados prevalecem sobre o __dict__ da instância; os descritores sem dados, não. Use __set_name__ para obter o nome do atributo. property, classmethod e staticmethod são todos descritores.
Perguntas Frequentes
A aula “Descritores: __get__, __set__, __delete__” é grátis?
Sim — o texto completo de “Descritores: __get__, __set__, __delete__” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Python Academy, atualize para CoddyKit PRO. O curso de Python Academy inclui 4 aulas no total.
O que vou aprender em “Descritores: __get__, __set__, __delete__”?
Implemente o protocolo de descritores para controlar o acesso a atributos. Você pratica Python Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Python Academy?
Nenhuma experiência prévia é necessária. Python Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.
Quanto tempo leva a aula “Descritores: __get__, __set__, __delete__”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Python Academy?
Sim. Cada aula de Python Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Como as classes Python são criadas
- Escrita de metaclasses personalizadas
- Descritores: __get__, __set__, __delete__
- __slots__ e otimização de memória