pyguides

__set_name__

__set_name__(self, owner, name)

Overview

__set_name__ is part of Python’s descriptor protocol. It is called automatically by the metaclass (type) after the class body finishes executing, once for each descriptor attribute defined on the class. Python passes the descriptor the class it belongs to and the attribute name it was assigned to.

This method was introduced in Python 3.6 via PEP 487, which also gave __init__ sublass hooks their modern form.

Signature

def __set_name__(self, owner, name):
    ...

Parameters:

ParameterTypeDescription
selfdescriptor instanceThe descriptor instance on the class
ownertypeThe class the descriptor is assigned to
namestrThe attribute name the descriptor is assigned to

Return type: None. This method mutates the descriptor’s internal state rather than returning a value.

Why It Exists

Descriptors in Python are objects with __get__ and optionally __set__ / __delete__ methods. When you write class Foo: bar = MyDescriptor(), the descriptor instance needs to know it was assigned to Foo under the name bar — but that information is not available inside the descriptor’s own __init__ when the class body is still executing.

__set_name__ solves this. Python calls it after the class body finishes, giving the descriptor a chance to store its owner and name for use in __get__, __set__, or any other method.

Without __set_name__, you would have to manually configure descriptors after the class is defined:

class Field:
    pass

class AuthorForm:
    name = Field()

# Old way — manually configure after class is created
Field.name = 'name'
Field.owner = AuthorForm

__set_name__ automates this, keeping descriptor configuration tied to class definition time.

Basic Example

Here is a descriptor that simply records what class and name it was assigned:

class Tracker:
    def __set_name__(self, owner, name):
        self.owner = owner
        self.name = name
    
    def __get__(self, instance, owner=None):
        if instance is None:
            return f"{self.owner.__name__}.{self.name}"
        return instance.__dict__.get(self.name, None)

class Point:
    x = Tracker()
    y = Tracker()

print(Point.x)   # Point.x
print(Point.y)   # Point.y

p = Point()
p.x = 10
p.y = 20
print(p.x)       # 10
print(p.y)       # 20

Each Tracker instance learns its owner (the class) and name (x or y) through __set_name__. The descriptor uses that stored name to look up values in the instance’s __dict__.

Practical Use Case: Form Field Registration

A common pattern in form libraries and ORMs is to have field descriptors that register themselves with the class. __set_name__ makes this automatic:

class CharField:
    def __init__(self):
        self.name = None
        self._owner = None
    
    def __set_name__(self, owner, name):
        self.name = name
        # Register this field on the owner class
        if not hasattr(owner, '_fields'):
            owner._fields = {}
        owner._fields[name] = self
    
    def __get__(self, instance, owner=None):
        if instance is None:
            return self
        return instance.__dict__.get(self.name, '')
    
    def __set__(self, instance, value):
        instance.__dict__[self.name] = value


class AuthorForm:
    name = CharField()
    email = CharField()

print(AuthorForm._fields)
# output: {'name': <CharField>, 'email': <CharField>}

After the AuthorForm class body finishes, AuthorForm._fields is already populated with all the field descriptors. The form class did not need to explicitly register anything — __set_name__ handled it.

Multiple Assignments: Same Instance, Different Names

One subtlety: if the same descriptor instance is assigned to multiple attributes, __set_name__ is called for each assignment, and the last call wins:

class Field:
    def __set_name__(self, owner, name):
        print(f"Called with owner={owner.__name__}, name={name}")
        self.current_name = name

shared = Field()

class MyForm:
    a = shared
    b = shared

# output: Called with owner=MyForm, name=a
# output: Called with owner=MyForm, name=b

print(shared.current_name)  # b

Both a and b point to the same Field instance. Python calls __set_name__ twice, and the second call (for b) overwrites the name set by the first call. If you need to track all names, store them in a list or set instead of overwriting a single attribute.

Inheritance: __set_name__ Only Fires Once Per Class Body

When you subclass a class with descriptors, __set_name__ is not called again for inherited descriptor instances. It fires only once — in the class body where the descriptor was originally defined:

class Base:
    field = Tracker()

class Child(Base):
    pass

# __set_name__ was called only for Base, not for Child

Inherited descriptors still work, but they retain the owner and name from the original class where they were defined.

__set_name__ and property

The built-in property descriptor uses __set_name__ internally. When you write:

class Foo:
    @property
    def bar(self):
        return self._bar

Python’s property implementation calls __set_name__ on its getter/setter/deleter functions to record fget.__name__ = 'bar', fset.__name__ = 'bar', and so on. This is how property knows what name to report in error messages and introspection output.

Summary

  • __set_name__ is called automatically by the metaclass after a class body finishes executing
  • It receives the owner class and the attribute name as arguments
  • It is the mechanism that lets descriptors learn their own name without manual post-class-configuration
  • Useful for building field registries, tracking descriptor metadata, and automating setup
  • Return type is None — the method mutates the descriptor’s state instead
  • It fires once per descriptor per class body, not per inheritance hierarchy

See Also

  • __call__ — Make instances callable like functions
  • __new__ — Control instance creation before __init__
  • __eq__ — Define equality comparison for your objects