ProgramaciónDesarrollador Python Senior

¿Qué son los descriptores en Python? ¿Cómo se implementan, para qué se utilizan, cuál es la diferencia con property y cuáles son los errores típicos en su uso?

Supere entrevistas con el asistente de IA Hintsage

Respuesta

Un descriptor es un objeto que implementa cualquiera de los métodos get, set y delete. Permite controlar el acceso a los atributos de una clase.

Métodos estándar de un descriptor:

  • __get__(self, instance, owner)
  • __set__(self, instance, value)
  • __delete__(self, instance)

Los descriptores pueden ser:

  • Clásicos (data descriptors) — implementan set, tienen prioridad sobre los atributos normales;
  • No clásicos (non-data descriptors) — implementan solo get, ceden ante el atributo normal.

Ejemplo de uso:

class OnlyPositive: def __init__(self): self._name = '_value' def __get__(self, instance, owner): return instance.__dict__[self._name] def __set__(self, instance, value): if value < 0: raise ValueError('El valor debe ser >= 0') instance.__dict__[self._name] = value class Account: value = OnlyPositive() def __init__(self, value): self.value = value acc = Account(10) acc.value = -1 # ¡ValueError!

property es solo un azúcar sintáctico para crear descriptores a nivel de clase.

Pregunta capciosa

¿Qué pasará si se crea un descriptor como atributo de instancia, en lugar de clase?

Muchos creen que el descriptor funcionará, pero no es así.

Respuesta correcta:

El descriptor funciona solo si se define directamente como un atributo de clase. Si se establece un descriptor como atributo de instancia, los métodos get/set no se llamarán — lógica normal de acceso al atributo.

Ejemplos de errores reales por desconocimiento de los matices del tema


Historia

Uso de property en lugar de un descriptor completo

Para la validación y otros atributos relacionados, el desarrollador utilizó un simple property, lo que llevó a la frecuente duplicación de lógica y a la imposibilidad de reutilizar código entre diferentes clases.


Historia

No respetar la inmutabilidad al reutilizar almacenamiento

El descriptor almacenaba datos en un atributo interno de su propia clase (self.x), y no en el almacenamiento interno de la instancia, lo que hacía que el atributo se volviera "compartido", y diferentes instancias de la clase sobrescribían los valores entre sí — los datos "fugaban" entre los objetos.


Historia

Confundido data descriptor y non-data descriptor

En una jerarquía compleja se omitió la implementación de set, lo que provocó que el atributo normal de la instancia sobrescribiera el descriptor, rompiendo todo el mecanismo de validación — el error no se manifestaba siempre, complicado de depurar.