5

I am getting maximum recursion depth error in the line if hasattr(self, '_ubuffer'). Can anyone see what am I doing wrong here? Whole code of the function is:

def __getattr__(self, name):
        if hasattr(self, '_ubuffer'):
            buffer = object.__getattribute__(self,'_ubuffer')
            if name in buffer.dtype.names:
                return buffer.data[name]
        return object.__getattribute__(self,name)
M_global
  • 207
  • 4
  • 15
  • possible duplicate of [Maximum recursion depth error with getattr](http://stackoverflow.com/questions/12163476/maximum-recursion-depth-error-with-getattr) – Bas Swinckels Aug 24 '14 at 12:50

3 Answers3

6

hasattr calls __getattr__ which would cause the recursion. From the docs:

hasattr(object, name) The arguments are an object and a string. The result is True if the string is the name of one of the object’s attributes, False if not. (This is implemented by calling getattr(object, name) and seeing whether it raises an exception or not.) [my emphasis]

One way to get around this problem might be to replace if hasattr(self, '_ubuffer') with something like if '_ubuffer' in self.__dict__.

Alex Riley
  • 169,130
  • 45
  • 262
  • 238
1

You can use the superclass implementation for regular attribute access:

def __getattr__(self, name):
    try:
        buffer = super(MyClass, self).__getattr__('_ubuffer')
    except AttributeError:
        raise AttributeError('Attribute {} not found.'.format(name))

    if name in buffer.dtype.names:
        return buffer.data[name]
    else:
        raise AttributeError('Attribute {} not found.'.format(name))
Maciej Gol
  • 15,394
  • 4
  • 33
  • 51
  • I like this way more. It is less intrusive (no need to dig up internal `__dict__` attribute), it is more portable since it also works for classes using `__slots__` instead of `__dict__`, and it is faster (it is recommended to sure `try ... catch ...` blocks instead of `if ... else ...` if it is never supposed to fail). – milembar Jan 27 '21 at 09:08
1

The method proposed by @maciej-gol is quite nice, but it does not handle the common case where the super class does not define custom __getattr__ (at least for Python3). To avoid such issue, it is better to check first using __getattribute__, which is always defined, and to fallback to _getattr_ if it fails.

I suggest doing this if you don't know in advance if the base class implements a custom __getattr__ method:

def __getattr__(self, name):
    try:
        buffer = self.__getattribute__('_ubuffer')
    except AttributeError:
        try:
            buffer = super().__getattr__('_ubuffer')
        except AttributeError:
            raise AttributeError('Attribute {} not found.'.format(name))

    if name in buffer.dtype.names:
        return buffer.data[name]
    raise AttributeError('Attribute {} not found.'.format(name))

But in most cases, just using __getattribute__ should be enough:

def __getattr__(self, name):
    try:
        buffer = self.__getattribute__('_ubuffer')
        if name in buffer.dtype.names:
            return buffer.data[name]
        raise AttributeError
    except AttributeError:
        raise AttributeError('Attribute {} not found.'.format(name))
milembar
  • 919
  • 13
  • 17