types.DynamicClassAttribute es un descriptor especializado que funciona como una propiedad en las instancias, pero permite que el acceso desde la clase sea dirigido al __getattr__ de su metaclase. Este comportamiento sirve para APIs donde el mismo nombre necesita significados distintos en la instancia y en la clase.
El caso más conocido está relacionado con Enum. Para usarlo correctamente conviene comprender descriptors, properties y metaclases.
Comportamiento básico
from types import DynamicClassAttribute
class Item:
def __init__(self, valor):
self._valor = valor
@DynamicClassAttribute
def valor(self):
return self._valor
print(Item(10).valor)
En una instancia, el descriptor se comporta de forma similar a property.
Acceso desde la clase
Cuando se solicita Item.valor, el descriptor levanta AttributeError. Una metaclase puede responder mediante __getattr__.
class Meta(type):
def __getattr__(cls, nombre):
if nombre == "valor":
return "valor de clase"
raise AttributeError(nombre)
class Item(metaclass=Meta):
@DynamicClassAttribute
def valor(self):
return 42
print(Item().valor)
print(Item.valor)
Diferencia frente a property
Una property normal accedida desde la clase suele devolver el descriptor. DynamicClassAttribute provoca intencionalmente la ruta de atributo ausente en el nivel de clase.
Protocolo descriptor
Los descriptors controlan acceso con __get__, __set__ y __delete__. Igual que property, esta clase admite getter, setter y deleter.
Requisito de metaclase
El comportamiento dinámico necesita __getattr__ en la metaclase. Un método de instancia con ese nombre no gestiona atributos ausentes del propio objeto clase.
Relación con Enum
Las implementaciones de Enum distinguen miembros, atributos de instancia y nombres resueltos sobre la clase enum. El mecanismo ayuda a separar esos namespaces.
Casos de uso
- Frameworks con namespaces administrados por metaclases.
- APIs con semánticas diferentes en clase e instancia.
- Sistemas similares a Enum.
- Infraestructura avanzada de proxies e introspección.
Cuándo evitarlo
La mayoría de las aplicaciones son más claras con nombres diferentes o métodos de clase explícitos. La resolución dinámica complica autocompletado, documentación, análisis estático y debugging.
Introspección estática
getattr puede ejecutar la resolución dinámica. Usa inspect.getattr_static o inspect.getmembers_static para inspeccionar el descriptor sin activarlo.
import inspect
descriptor = inspect.getattr_static(Item, "valor")
print(type(descriptor))
Tipado
Los type checkers pueden no modelar perfectamente nombres generados por metaclases. Mantén la API pública sencilla y considera stubs, protocolos u overloads.
Errores comunes
- Definir
__getattr__en la clase en lugar de la metaclase. - Esperar que el acceso de clase devuelva el descriptor.
- Crear recursión en la resolución dinámica.
- Usarlo cuando una property normal basta.
- Ignorar documentación e introspección.
Buenas prácticas
Limita el recurso a infraestructura, prueba ambos caminos de acceso, conserva mensajes útiles de AttributeError y documenta el doble significado. Consulta las guías internas sobre módulo types, Enums e introspección.
Conclusión
DynamicClassAttribute combina una propiedad de instancia con resolución dinámica de clase. Es potente en frameworks con metaclases, pero debe usarse con moderación.







