Search code examples
pythondescriptor

Why does an attribute being created through an object descriptor need to have a different value from its original name


I have this descriptor class that I wrote that gives me this error when I try to use it in a different class: RecursionError: maximum recursion depth exceeded

class x:
    def __init__(...):
        ...

    def __set_name__(self, _, name):
        self.internal_name = name

    def __get__(self, instance, _):
        if instance is None:
            return
        else:
            return getattr(instance, self.internal_name)

    def __set__(self, instance, value):
        if instance is None:
            return
        else:
            setattr(instance, self.internal_name, value)

I found that by changing my __set_name__ method to this I no longer get this issue:

def __set_name__(self, _, name):
    self.internal_name = f'_{name}'

But I am unsure as to why this is the case. Could you help explain why I need to make sure the name is different from the value I get back from __set_name__


Solution

  • This is because you're calling:

    getattr(instance, self.internal_name)
    

    with self.internal_name having the same value as the name of the descriptor instance being accessed, so getattr invokes another call to __get__ of the same descriptor instance with the same instance, which again calls getattr(instance, self.internal_name), resulting in infinite recursion. The same situation applies to your call to setattr.

    By naming self.internal_name differently from the name of the descriptor instance, getattr(instance, self.internal_name) no longer refers to the descriptor instance for a value and would properly fallback to looking up the the attribute name in the members dict of the instance and the types in the MRO.