0Pricing
Python Academy · Aula

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 ... 42

Descritores 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)   # data

Assinatura 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   # AttributeError

Funçõ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 function

Descritor 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__ = fset

Ordem 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 AttributeError

Verificaçã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

  1. Como as classes Python são criadas
  2. Escrita de metaclasses personalizadas
  3. Descritores: __get__, __set__, __delete__
  4. __slots__ e otimização de memória
← Voltar para Python Academy