PythonProgrammingSenior Python Engineer

By what automatic invocation does **Python**'s descriptor protocol capture the assigned attribute name during class body execution, and why does this eliminate the need for explicit name repetition in descriptor declarations?

Pass interviews with Hintsage AI assistant

Answer to the question

Python invokes the optional __set_name__(self, owner, name) method on descriptor objects automatically during the class creation process, specifically after the class body executes but before the class object is finalized by the metaclass. When type.__new__ processes the namespace dictionary, it detects any values possessing a __set_name__ attribute and calls this hook, passing the class under construction and the corresponding attribute key. This mechanism allows the descriptor to introspect and store its own name without requiring developers to pass it as a redundant string argument to the constructor. Introduced in PEP 487 for Python 3.6, this protocol is essential for building declarative frameworks like ORMs or data validators that need to know their attribute names for serialization or database mapping purposes.

class AutoNamedField: def __set_name__(self, owner, name): self.name = name self.owner = owner def __get__(self, obj, objtype=None): if obj is None: return self return obj.__dict__.get(self.name) class Model: user_id = AutoNamedField() # __set_name__ called with name='user_id' automatically

Situation from life

While designing a lightweight data validation library, the team faced a recurring source of bugs where developers declared schema fields using email = Validator('email'), but during refactoring would rename the attribute without updating the string literal, causing runtime mismatches between the API and the database. This explicit repetition violated the DRY principle and created maintenance friction across a hundred-model codebase.

One solution evaluated was implementing a custom metaclass that iterates over the class dictionary upon creation, identifies Validator instances by type checking, and manually injects the attribute name by comparing object identity with namespace keys. This approach functions correctly but introduces significant complexity by requiring careful metaclass conflict resolution when users inherit from multiple framework classes, and it incurs unnecessary overhead during the import phase for every class definition.

Another alternative considered was employing a class decorator applied after class creation that walks the __dict__ via vars() and patches the name attribute onto descriptor instances retrospectively. While this avoids metaclass proliferation, it separates the naming logic from the descriptor declaration itself, making the codebase harder to understand and maintain, and it fails to handle descriptors added dynamically after class creation without additional hooks.

The chosen solution implemented the __set_name__ protocol directly within the Validator class. This eliminated the need for explicit string arguments entirely, allowing clean declarations like email = Validator(), and removed the dependency on complex metaclasses or decorators. The result was a robust, declarative API that reduced refactoring risk by ensuring attribute names remained synchronized with variable identifiers, while significantly simplifying the library's architecture and improving compatibility with diverse user inheritance patterns.

What candidates often miss

When exactly does the interpreter invoke __set_name__ during the class creation lifecycle?

Many candidates mistakenly believe the hook fires during the descriptor's own __new__ or __init__ methods, or alternatively during instance initialization. In reality, Python's type.__new__ triggers __set_name__ after executing the class body—which populates the namespace dictionary—but before returning the fully formed class object. Specifically, the interpreter iterates over the namespace items, checks for the presence of __set_name__ using hasattr, and invokes it with the owner class and the attribute key. This timing is critical because it allows the descriptor to know its final name before any subclasses or instances are created, yet after all class-level assignments have been processed.

What happens if a descriptor is assigned to a class dynamically after the class has been created?

A common misconception is that __set_name__ is called whenever a descriptor is attached to a class attribute under any circumstances. However, the hook is only invoked during the initial class creation process managed by the type metaclass. If you subsequently execute setattr(MyClass, 'new_attr', MyDescriptor()) on an existing class, Python will not automatically trigger __set_name__. Consequently, the descriptor remains unaware of its attribute name unless you manually invoke descriptor.__set_name__(MyClass, 'new_attr'), which is frequently overlooked in dynamic schema generation scenarios and leads to subtle bugs where the descriptor cannot locate itself in the class hierarchy.

How does __set_name__ behave when descriptors are inherited from parent classes?

Candidates often struggle with whether __set_name__ fires again for inherited descriptors in subclasses. The method is invoked only once, at the moment the descriptor is assigned in the class body of the class where it originally appears. When a subclass inherits the descriptor, it receives the same instance object that was already named in the parent; Python does not re-invoke __set_name__ for the subclass because the descriptor object itself has not been newly assigned in the subclass namespace—it is merely accessed via the MRO. This means descriptors relying on __set_name__ to store per-class metadata must use weak references or separate storage keyed by owner class, rather than assuming the owner argument in __set_name__ represents all classes that might eventually access the descriptor.