Yagiz Degirmenci
Yagiz Degirmenci

Reputation: 20716

When __set_name__ is useful in python?

I saw a tweet from Raymond Hettinger yesterday. He used __set_name__.

When I define __set_name__ method for my Class, the name becomes the instance's name. The owner became Foo, which is also expected but I couldn't figure out when and how this is useful.

class Bar:
    def __set_name__(self, owner, name):
        print(f'{self} was named {name} by {owner}')

class Foo:
    x = Bar()
    y = Bar()

That prints

<__main__.Bar object at 0x7f48f2968820> was named x by <class '__main__.Foo'>
<__main__.Bar object at 0x7f48f2968c70> was named y by <class '__main__.Foo'>

Upvotes: 14

Views: 9370

Answers (2)

Ken Jiiii
Ken Jiiii

Reputation: 730

Raymond Hettinger uses this construct as a tool for structural pattern matching. See the documentation of his talk at PyIT 2022.

class FuncCall:
    def __init__(self, func):                                                                                                                                                                                            
        self.func = func

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

    def __get__(self, obj, objtype=None):
        return self.func(self.name)

class A:
    x = FuncCall(ord)

A.x

FuncCall is a descriptor to convert fc.name to func(name).

Upvotes: 1

Amir reza Riahi
Amir reza Riahi

Reputation: 2460

As @juanpa provided a brief explanation, it is used to know the variable's name and class. One of its use cases is for logging. When you want to log the variable's name. This example was in descriptor's HowTo.

import logging

logging.basicConfig(level=logging.INFO)

class LoggedAccess:

    def __set_name__(self, owner, name):
        self.public_name = name
        self.private_name = '_' + name

    def __get__(self, obj, objtype=None):
        value = getattr(obj, self.private_name)
        logging.info('Accessing %r giving %r', self.public_name, value)
        return value

    def __set__(self, obj, value):
        logging.info('Updating %r to %r', self.public_name, value)
        setattr(obj, self.private_name, value)

class Person:

    name = LoggedAccess()                # First descriptor instance
    age = LoggedAccess()                 # Second descriptor instance

    def __init__(self, name, age):
        self.name = name                 # Calls the first descriptor
        self.age = age                   # Calls the second descriptor

    def birthday(self):
        self.age += 1

Upvotes: 12

Related Questions