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