0Pricing
Python Academy · Lección

Descriptores: __get__, __set__, __delete__

Implemente el protocolo de descriptores para controlar el acceso a atributos.

Descriptores: __get__, __set__, __delete__ es una lección gratuita de Python Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Python Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Python Academy incluye 4 lecciones en total.

¿Qué es un descriptor?

Un descriptor es un objeto que define __get__, __set__ o __delete__ y se asigna como atributo de una clase. Python llama a estos métodos al acceder al 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

Descriptores de datos y no de datos

Los descriptores de datos definen __set__ o __delete__ y tienen prioridad sobre el __dict__ de la instancia. Los descriptores que no son de datos, que solo definen __get__, ceden ante __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

Firma de __get__

__get__(self, obj, objtype): obj es la instancia, o None cuando se accede desde la clase; objtype es la clase.

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__ y validación

Implemente __set__(self, obj, value) para validar o transformar un valor antes de almacenarlo.

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__

Python 3.6+ llama a __set_name__(owner, name) en el descriptor cuando se asigna a una clase, lo que le permite acceder al nombre de su propio 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 eliminar 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

Las funciones son descriptores que no son de datos

Las funciones implementan __get__ para devolver un método ligado cuando se accede a ellas desde una instancia. Así funciona la vinculación de métodos en 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 y staticmethod son descriptores

classmethod y staticmethod son clases de descriptores integradas que envuelven funciones y modifican el valor devuelto 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

Descriptor de atributos diferidos

Construya un descriptor que calcule un valor una vez y lo almacene en caché por instancia.

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 es un descriptor

property es en sí un descriptor de datos implementado en C. Comprender los descriptores le ayuda a entender cómo funcionan @property, @classmethod y @staticmethod.

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

Orden de búsqueda de descriptores

Orden de búsqueda de atributos en Python: 1) descriptores de datos del tipo (MRO), 2) el __dict__ de la instancia, 3) descriptores que no son de datos y variables de clase.

# 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

Comprobación rápida

¿Qué tipo de descriptor tiene prioridad sobre el __dict__ de la instancia?

Repaso

Los descriptores interceptan el acceso a los atributos mediante __get__, __set__ y __delete__. Los descriptores de datos ocultan el __dict__ de la instancia; los que no son de datos no lo hacen. Use __set_name__ para conocer el nombre del atributo. property, classmethod y staticmethod son descriptores.

Preguntas frecuentes

¿La lección «Descriptores: __get__, __set__, __delete__» es gratis?

Sí — el texto completo de «Descriptores: __get__, __set__, __delete__» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Python Academy, actualiza a CoddyKit PRO. El curso de Python Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Descriptores: __get__, __set__, __delete__»?

Implemente el protocolo de descriptores para controlar el acceso a atributos. Practicas Python Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Python Academy?

No se requiere experiencia previa. Python Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Descriptores: __get__, __set__, __delete__»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Python Academy?

Sí. Cada lección de Python Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Cómo se crean las clases de Python
  2. Escritura de metaclases personalizadas
  3. Descriptores: __get__, __set__, __delete__
  4. __slots__ y optimización de memoria
← Volver a Python Academy