Python: how to implement __getattr__()?

I like to take this therefore.

I took it from somewhere, but I don't remember where.

class A(dict):
    def __init__(self, *a, **k):
        super(A, self).__init__(*a, **k)
        self.__dict__ = self

This makes the __dict__ of the object the same as itself, so that attribute and item access map to the same dict:

a = A()
a['a'] = 2
a.b = 5
print a.a, a['b'] # prints 2 5

Late to the party, but found two really good resources that explain this better (IMHO).

As explained here, you should use self.__dict__ to access fields from within __getattr__, in order to avoid infinite recursion. The example provided is:

def __getattr__(self, attrName):
  if not self.__dict__.has_key(attrName):
     value = self.fetchAttr(attrName)    # computes the value
     self.__dict__[attrName] = value
  return self.__dict__[attrName]

Note: in the second line (above), a more Pythonic way would be (has_key apparently was even removed in Python 3):

if attrName not in self.__dict__:

The other resource explains that the __getattr__ is invoked only when the attribute is not found in the object, and that hasattr always returns True if there is an implementation for __getattr__. It provides the following example, to demonstrate:

class Test(object):
    def __init__(self):
        self.a = 'a'
        self.b = 'b'

    def __getattr__(self, name):
        return 123456

t = Test()
print 'object variables: %r' % t.__dict__.keys()
#=> object variables: ['a', 'b']
print t.a
#=> a
print t.b
#=> b
print t.c
#=> 123456
print getattr(t, 'd')
#=> 123456
print hasattr(t, 'x')
#=> True     

class MyClass(object):

    def __init__(self):
        self.data = {'a': 'v1', 'b': 'v2'}

    def __getattr__(self, attr):
        return self.data[attr]

>>> ob = MyClass()
>>> v = ob.a
>>> v
'v1'

Be careful when implementing __setattr__ though, you will need to make a few modifications:

class MyClass(object):

    def __init__(self):
        # prevents infinite recursion from self.data = {'a': 'v1', 'b': 'v2'}
        # as now we have __setattr__, which will call __getattr__ when the line
        # self.data[k] tries to access self.data, won't find it in the instance 
        # dictionary and return self.data[k] will in turn call __getattr__
        # for the same reason and so on.... so we manually set data initially
        super(MyClass, self).__setattr__('data', {'a': 'v1', 'b': 'v2'})

    def __setattr__(self, k, v):
        self.data[k] = v

    def __getattr__(self, k):
        # we don't need a special call to super here because getattr is only 
        # called when an attribute is NOT found in the instance's dictionary
        try:
            return self.data[k]
        except KeyError:
            raise AttributeError

>>> ob = MyClass()
>>> ob.c = 1
>>> ob.c
1

If you don't need to set attributes just use a namedtuple eg.

>>> from collections import namedtuple
>>> MyClass = namedtuple("MyClass", ["a", "b"])
>>> ob = MyClass(a=1, b=2)
>>> ob.a
1

If you want the default arguments you can just write a wrapper class around it:

class MyClass(namedtuple("MyClass", ["a", "b"])):

    def __new__(cls, a="v1", b="v2"):
        return super(MyClass, cls).__new__(cls, a, b)

or maybe it looks nicer as a function:

def MyClass(a="v1", b="v2", cls=namedtuple("MyClass", ["a", "b"])):
    return cls(a, b)

>>> ob = MyClass()
>>> ob.a
'v1'

class A(object):
  def __init__(self):
     self.data = {'a': 'v1', 'b': 'v2'}
  def __getattr__(self, attr):
     try:
       return self.data[attr]
     except Exception:
       return "not found"


>>>a = A()
>>>print a.a
v1
>>>print a.c
not found

Tags:

Python