Python descriptors decorators (properties) cause an infinite recursive problem to share

  • 2020-04-02 13:51:31
  • OfStack

To share a little problem I just ran into, I have a piece of python code that looks something like this:


# coding: utf-8 class A(object):     @property
    def _value(self):
#        raise AttributeError("test")
        return {"v": "This is a test."}     def __getattr__(self, key):
        print "__getattr__:", key
        return self._value[key] if __name__ == '__main__':
    a = A()
    print a.v

When run, you get the correct result

__getattr__: v
This is a test.

But notice, if you take

#        raise AttributeError("test")


When the comment on this line is removed, an AttributeError exception is thrown in the _value method, and things get a little weird. Instead of throwing an exception while the program is running, it enters an infinite recursion:


File "attr_test.py", line 12, in __getattr__
    return self._value[key]
  File "attr_test.py", line 12, in __getattr__
    return self._value[key]
RuntimeError: maximum recursion depth exceeded while calling a Python object

A multi-party lookup reveals a property decorator problem. Property is actually a descriptor. The following text can be found in python doc:


object.__get__(self, instance, owner) Called to get the attribute of the owner class (class attribute access) or of an instance of that class (instance attribute access). owner is always the owner class, while instance is the instance that the attribute was accessed through, or None when the attribute is accessed through the owner. This method should return the (computed) attribute value or raise an AttributeError exception.

Thus, when the user accesses._value, the AttributeError is thrown and the method is called with successive getattr__ to try to get it. So it becomes an infinite recursion.

This problem doesn't seem complicated, but it can be difficult to debug when your _value method is implicitly throwing an AttributeError.


Related articles: