Olivier Melançon
Olivier Melançon

Reputation: 22294

How to get attribute name of a descriptor from within __get__?

When implementing a descriptor, it is possible to use __set_name__ to register under which attribute name the descriptor was set.

Although, suppose we were to set more than one attribute to the same descriptor, then there seems to be no way to know through which name the descriptor was accessed in the __get__ and __set__ method.

Code

class Prop:
    def  __init__(self):
        self._names = {}

    def __set_name__(self, owner, name):
        print(f'Attribute \'{name}\' was set to a Prop')

        if owner in self._names:
            self._names[owner].append(name)
        else:
            self._names[owner] = [name]

    def __get__(self, instance, owner):
        print(f'Prop was accessed through one of those: {self._names[owner]}')

prop = Prop()

class Foo:
    bar = prop
    baz = prop

foo = Foo()
foo.baz

Output

Attribute 'bar' was set to a Prop
Attribute 'baz' was set to a Prop
Prop was accessed through one of those: ['bar', 'baz']

Is there a clean and general way to know from which attribute a descriptor was accessed?

Upvotes: 0

Views: 641

Answers (1)

MSeifert
MSeifert

Reputation: 152637

Is there a clean and general way to know from which attribute a descriptor was accessed?

No, there is no clean and general way.

However if you want a dirty hack (please don't do that!) you can avoid the descriptor protocol and just pass in the name through which it was accessed manually:

class Descriptor:
    def __get__(self, instance, owner, name=None):
        print(f"Property was accessed through {name}")
        return 'foo'

p = Descriptor()

class Test:
    a = p
    b = p

    def __getattribute__(self, name):
        for klass in type(self).__mro__:
            if name in klass.__dict__ and isinstance(klass.__dict__[name], Descriptor):
                return klass.__dict__[name].__get__(self, klass, name)
        else:
            return object.__getattribute__(self, name)

Upvotes: 2

Related Questions