Search code examples
pythonpython-2.7infinitegetattr

Infinite loop using __getattr__ and getattr


Okay, I'm having a bit of a problem and I'm not quite sure why its occurring the way that it is. What I'm trying to do is allow access to attributes on an object via snake_case or camelCase in python 2.7.X I thought that __getattr__ was the correct way to do this, but clearly I'm mistaken and I'm not ex

So here's my simplified implementation. In this scenario, no matter what attribute you access, I want foo.x to be returned.

class Foo(object):
  def __init__(self, x):
    self.x = x

  def __getattr__(self, name):
    if hasattr(self, name):
      return getattr(self, name)
    elif hasattr(self, 'x'):
      return getattr(self, 'x')
    else:
      raise AttributeError, n

Now if I do:

f = Foo('myX')
f.x             
# => "myX"

However, I don't understand why:

f.y

Loops forever. Interestingly, when I put print "__getattr__ called with : %s" % name at the first line of the __getattr__, it appears that when f.y gets called, the getattr(self, 'x') line does actually get called, but then it doesn't look like getattr is ever actually called with 'x' instead, __getattr__ still says that it is getting called with "y."

I've been looking around, and I think it has something to do with my misunderstanding of how the getattr works. Any advice or documentation that you all have to point me to would be very beneficial. Thanks in advance.


Solution

  • hasattr() is implemented as a call to getattr() and returns False if an exception was raised.

    __getattr__ is only called if your instance doesn't have the attribute; it is a fallback. Using hasattr() in __getattr__ is entirely pointless.

    Since f.x exists, Foo.__getattr__ is never called. f.y does not exist, so Foo.__getattr__(f, 'y') is called, which calls hasattr(f, 'y') which calls getattr(f, 'y') which calls Foo.__getattr__(f, 'y') because the y attribute doesn't exist, etc.

    From the object.__getatt__() documentation:

    Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self).

    and from the hasattr() function documentation:

    (This is implemented by calling getattr(object, name) and seeing whether it raises an exception or not.)