Back to Blog
Python

How python **set_name** Works in Descriptors

python **set_name**: Learn how Python's __set_name__ descriptor method captures the attribute name during class creation, enabling self-aware validators and ORM fields.

descriptorsPython classesPEP 487attribute bindingclass creation
Illustration of a Python descriptor object receiving its attribute name during class creation.

When you define a descriptor in Python, you often need to know the attribute name it is assigned to. Before PEP 487, you had to pass that name manually to the descriptor's constructor, which was repetitive and error-prone. The __set_name__ method solves this by letting the descriptor learn its attribute name automatically when the class is created. This article explains how python set_name works, when it is called, and how to use it effectively in your own descriptors.

What Is set_name and Why Does It Exist?

A descriptor is any object that implements __get__, __set__, or __delete__. Descriptors power properties, methods, and many ORM fields. The problem is that a descriptor object, when placed in a class body, has no built-in way to know which attribute name it is bound to. For example, if you write name = Validator(), the Validator instance does not know it is called name. You could pass the name explicitly: name = Validator('name'), but that duplicates information and invites typos.

__set_name__ was introduced in Python 3.6 via PEP 487. It is called automatically when a class is created, giving the descriptor both the class object and the attribute name. This makes descriptors self-aware and reduces boilerplate.

How set_name Is Called During Class Creation

When Python executes a class statement, it runs the class body to build a namespace, then calls the metaclass (usually type) to create the class object. As part of that process, type.__new__ scans the namespace for any object that has a __set_name__ method and calls it with two arguments: the class being created and the attribute name. The call happens after the class body is executed but before the class is fully bound to its name.

The order of these calls matches the order in which attributes appear in the class namespace. This is deterministic, but you should not rely on it for cross-descriptor communication. Each descriptor receives only its own name and the class, not information about other descriptors.

Implementing a Descriptor with set_name

Here is a minimal descriptor that uses __set_name__ to store its attribute name:

class Validator: def __set_name__(self, owner, name): self.name = name def __get__(self, obj, objtype=None): if obj is None: return self return obj.__dict__.get(self.name) def __set__(self, obj, value): if not isinstance(value, str): raise TypeError(f"{self.name} must be a string") obj.__dict__[self.name] = value

When you use this descriptor in a class:

class Person: name = Validator() email = Validator()

The __set_name__ method sets self.name to 'name' for the first instance and 'email' for the second. No manual name passing is needed. The descriptor can then use self.name in error messages, logging, or internal storage.

Practical Use Cases: Self-Aware Validators and ORM Fields

The most immediate benefit is eliminating repetitive constructor arguments. Without __set_name__, you would write name = Validator('name') and email = Validator('email'). With it, the descriptor knows its own name automatically. This pattern appears in many real-world libraries:

  • ORMs use descriptors to map class attributes to database columns. The descriptor needs the attribute name to build queries and map results.
  • Form frameworks use descriptors to bind form fields to model attributes.
  • Configuration systems use descriptors to track which settings are accessed.

For example, a simple ORM-like field could use __set_name__ to store the column name:

class Field: def __set_name__(self, owner, name): self.column = name def __get__(self, obj, objtype=None): if obj is None: return self return obj._values.get(self.column) def __set__(self, obj, value): obj._values[self.column] = value

This keeps the mapping logic in one place and avoids repeating the column name in every field declaration.

Interaction with Inheritance and Metaclasses

__set_name__ is called whenever a class is created, including subclasses. If a descriptor is inherited, it is called again with the subclass and the same attribute name. This can be useful if you need to reset per-subclass state. For instance, a descriptor that caches values might clear its cache when a subclass is defined.

Metaclasses can also call __set_name__ manually, but this is rarely necessary because the default type already does it. If you define a custom metaclass, you must ensure it calls super().__new__ or otherwise replicates the default behavior, or __set_name__ may not be invoked.

One subtlety: __set_name__ is only called for descriptors assigned in the class body. If you assign a descriptor to an instance, __set_name__ is not called. That is by design; the method is meant to capture the class-level attribute name.

Common Pitfalls and Edge Cases

A common mistake is overriding __set_name__ in a subclass of a descriptor and forgetting to call super().__set_name__. If you do that, the base descriptor's name is never set, and it may break. Always call the parent method unless you have a specific reason not to.

Another edge case involves multiple descriptors in the same class. The order of __set_name__ calls follows the class namespace order, but if you need to coordinate between descriptors, do it explicitly in the class body or metaclass, not by relying on call order.

Also note that __set_name__ receives the class object as its first argument, not an instance. If you need to store per-class data, you can attach it to the class itself or use a separate registry.

Performance and Maintainability Considerations

The performance cost of __set_name__ is negligible because it runs once per class creation, not per instance access. The real gain is maintainability. By removing the need to pass attribute names manually, you reduce the chance of typos and keep the descriptor logic self-contained. When you add a new attribute to a class, the descriptor automatically picks up the correct name without any extra code.

This is especially valuable in large codebases where descriptors are used extensively. It also makes the descriptor more robust because the name is always in sync with the attribute it is assigned to. If you rename an attribute, the descriptor updates automatically, which would not happen if the name were hardcoded in the constructor.

In summary, __set_name__ is a small but powerful addition to the descriptor protocol. It makes descriptors self-aware and simplifies the design of libraries that rely on attribute binding. Understanding how it works and when it is called helps you write cleaner, more maintainable Python code.

python **set_name** in Descriptors Explained | RYUSLOG DEV