How types.DynamicClassAttribute Works in Python

Python's types.DynamicClassAttribute is a descriptor that routes attribute access differently depending on whether the attribute is read from an instance or from the class itself. While accessing the attribute via an instance invokes a defined getter function just like a standard @property, accessing it directly from the class raises an AttributeError, allowing the lookup to fall back to the metaclass's __getattr__ method. This article explains the internal mechanics of this descriptor, why it is used, and how it enables dynamic class-level behavior in Python libraries such as enum.

The Standard Property Limitation

To understand DynamicClassAttribute, consider how Python's built-in @property behaves:

class Example:
    @property
    def value(self):
        return 42

obj = Example()
print(obj.value)      # Returns: 42
print(Example.value)  # Returns: <property object at 0x...>

When evaluated on an instance, a standard descriptor's __get__(instance, owner) receives the instance and returns the computed result. When evaluated on the class (Example.value), instance is None, and the descriptor returns self—the property object itself.

This behavior creates a problem when a class needs to expose an attribute on instances while allowing the class itself to handle that same attribute name dynamically (for example, through a metaclass). Because the property descriptor exists in Example.__dict__, Python's lookup rules find it first, preventing any metaclass fallback.

How DynamicClassAttribute Routes Access

types.DynamicClassAttribute modifies the descriptor protocol (__get__, __set__, __delete__) to bifurcate access between instances and classes:

  1. Instance Access (instance is not None):
    The descriptor acts like a standard @property. It invokes the instance getter (self.fget(instance)) and returns the resulting value.

  2. Class Access (instance is None):
    Instead of returning the descriptor object itself, DynamicClassAttribute.__get__ raises an AttributeError (unless a custom class getter is explicitly set).

In Python, when a descriptor's __get__ method raises an AttributeError during class-level attribute lookup (Class.attribute), Python catches the exception and falls back to searching the metaclass. If the metaclass defines a __getattr__ method, Python calls it with the attribute name.

The Enum Use Case

The primary motivation for types.DynamicClassAttribute in Python's standard library is the enum module.

Every enum member has name and value attributes:

from enum import Enum

class Status(Enum):
    PENDING = "pending"
    COMPLETED = "completed"

Here, Status.PENDING is an instance of Status. Accessing Status.PENDING.name returns "PENDING".

However, what happens if an enum defines a member named name or value?

class Fields(Enum):
    name = "Field Name"
    value = "Field Value"

If name were a standard @property:

Because Enum defines name and value as DynamicClassAttribute instances:

Practical Implementation Example

You can observe this routing mechanism directly by implementing a custom metaclass alongside DynamicClassAttribute:

from types import DynamicClassAttribute

class Meta(type):
    def __getattr__(cls, name):
        return f"Metaclass fallback for attribute: {name}"

class CustomModel(metaclass=Meta):
    def __init__(self, item):
        self._item = item

    @DynamicClassAttribute
    def item(self):
        return f"Instance item: {self._item}"

# Instance access routes to the getter function
instance = CustomModel("data_payload")
print(instance.item)
# Output: Instance item: data_payload

# Class access triggers AttributeError in the descriptor and falls back to Meta.__getattr__
print(CustomModel.item)
# Output: Metaclass fallback for attribute: item

By purposefully failing class-level descriptor resolution, types.DynamicClassAttribute hands control over to the metaclass, allowing clean separation between instance-level properties and class-level namespace semantics.