I've always known about the
_getattr_ function on classes in Python and how it could theoretically be used. However, I never had a real need to implement it, and so I had never actually implemented
__getattr__. For whatever reason, it was a tad more difficult that I thought, so I figured I'd share an example with you'all.
In case you don't already know, the idea is that any time any code makes are reference to an attribute a class (e.g.,
obj.x ),
__getattr__ gets called to fetch or compute the value of the attribute. This function can do almost anything, but you must be careful when making references to attributes, because that will trigger a recursive call to
__getattr__.
In my case, I was writing some code for unit testing. I needed to create a mock object that's used to store configuration information for the system. In the real object, every configuration attribute is initialized from an ini file parsed by
ConfigParser in the constructor. For testing, didn't want to have a huge configuration file for every test. So, I wanted to create a system that performed lazy initialization of the data attributes - i.e., only look in the ini file if we actually need a given item, and if the attribute is never referenced, we never need to fetch it from the ini file. Therefore, the ini file only needs the attributes that are actually used by a given test. Implementing
__getattr__ is the way to hook into the process to provide this lazy initialization.
The basic outline/algorithm is:
- If the attribute already exists on self, return that value
- Fetch/compute the missing value
- Store the value on self for subsequent use
- Return the value
The key to making this work and avoiding infinite recursion is the
__dict__ attribute, which is a (regular) dictionary, the keys of which are attributes that exist on the object and the values are the values of the attributes. We can access these keys and values without going through
__getattr__, thus avoiding recursion.
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]
It's pretty straightforward. In retrospect, I'm not sure what tripped me up when I first went to implement it. In the end, the
fetchAttr function ended up being pretty fancy, but I'll write more about that later. You gotta love a dynamic language like Python that makes this as simple as it is, even if it does require a bunch of underscores.
Enjoy,
Charles.