Python

When Descriptors Get Smart with __set_name__

Learn how Python's __set_name__ method lets descriptors automatically know their attribute names, removing manual string passing and reducing bugs in reusable validation and logging tools.

August 2026 5 min read 21 views 0 hearts

Here’s the article you requested, written for PythonSkillset.com.


When Descriptors Get Smart with __set_name__

You’ve probably heard of Python descriptors — they’re the secret sauce behind @property, classmethod, and staticmethod. But there’s one humble method that makes descriptors extra smart: __set_name__.

It saves you from passing attribute names manually. And if you’ve ever built reusable validation, logging, or type-checking tools, this little feature can clean up your code a lot.

The problem it solves

Imagine you have a descriptor for ensuring an attribute is always a positive integer:

class PositiveInt:
    def __get__(self, obj, objtype=None):
        return obj.__dict__.get(self.name)

    def __set__(self, obj, value):
        if not isinstance(value, int) or value <= 0:
            raise ValueError("Must be positive int")
        obj.__dict__[self.name] = value

The issue? The descriptor doesn’t know its own attribute name inside the class. So you end up doing something like:

class Person:
    age = PositiveInt("age")  # yuck, repeated name

That’s fragile and annoying. If you rename the attribute, you have to remember to change the string too.

Enter __set_name__

When Python creates the class, it calls __set_name__ on each descriptor, passing it the class and the attribute name. So you can write:

class PositiveInt:
    def __set_name__(self, owner, name):
        self.public_name = name
        self.private_name = "_" + name

    def __get__(self, obj, objtype=None):
        return getattr(obj, self.private_name)

    def __set__(self, obj, value):
        if not isinstance(value, int) or value <= 0:
            raise ValueError("Must be positive int")
        setattr(obj, self.private_name, value)

Now this works without any manual naming:

class Person:
    age = PositiveInt()
    score = PositiveInt()

p = Person()
p.age = 30   # works
p.score = -5 # raises ValueError

No more "age" strings. The descriptor figures it out automatically.

A real-world flavor

At PythonSkillset, we saw a team using this pattern for a config validator. Every config field had type constraints, min/max, and default values. Without __set_name__, they had a huge dictionary mapping field names to validators. With it, each validator knew its own field just by being assigned inside the class.

Here’s a simplified version:

class ConfigField:
    def __set_name__(self, owner, name):
        self.name = name

    def __get__(self, obj, objtype=None):
        return obj.__dict__.get(self.name, self.default)

    def __set__(self, obj, value):
        if not self.validate(value):
            raise ValueError(f"Invalid value for {self.name}")
        obj.__dict__[self.name] = value

    def validate(self, value):
        raise NotImplementedError

Then you subclass for specifics like IntField, StringField, etc. No manual wiring needed.

How it fits the bigger picture

__set_name__ works for all descriptors, not just data descriptors. You can use it in a logging decorator too:

class LoggedAttribute:
    def __set_name__(self, owner, name):
        self.name = name

    def __get__(self, obj, objtype=None):
        print(f"Accessing {self.name}")
        return obj.__dict__.get(self.name)

Every time someone reads the attribute, it logs the name automatically.

A quick gotcha

__set_name__ is called once when the class is created. If you dynamically add a descriptor to a class later (like MyClass.attr = SomeDescriptor()), __set_name__ won’t fire. For that, you’d need to call it manually. But in practice, that’s rare.

The takeaway

If you ever write a descriptor, use __set_name__ to capture its own attribute name. It removes redundancy and prevents bugs when renaming. That’s a small detail that makes a big difference in maintainable code.

Python keeps giving us these elegant helpers. This one is definitely worth keeping in your back pocket.

Comments

Questions, corrections, and tips stay visible for everyone reading this page.

0 in thread

Join the discussion

Shown next to your comment.

Up to 4,000 characters

No comments yet

Be the first to leave a note — it helps the next reader.