mccatnm mccatnm - 1 year ago 127
Python Question

Python __getattr__ to create attr if it does not exist and return it

A little background: You'll notice my comments describe what I'll go through later. Let's say I have the following object...

import os
import sys

class ContainerField(object):
''' An attribute/object storage device '''
def __init__(self, field=None, value=None):
self.m_field = field
self.m_value = value

def __getattr__(self, key):
What can we do here that runs the .get() command but -only- if the key
does not exist.
# super(ContainerField, self).__getattr__(key)

def __call__(self):
return self.get()

def value(self):
return self.m_value

def setValue(self, value):
self.m_value = value

def _recurseSetAttr(self, attr, values):
'''Generate our attributes/objects and store them succinctly.'''
# Container
# \_Container
# \_Container
# \_Container...
for field, value in values.items():
if not hasattr(attr, field):
# field type is known from model caching
ContainerField(value=value, field=field_type(field)))

fdbf = getattr(attr, field)
if isinstance(value, dict):
self._recurseSetAttr(fdbf, value)

def get(self):
# Create the new object from scratch and proliferate it's
# attributes recursively. 'values' come in the form of a
# dictionary that we can then use to setattr().
# So... Create container, set value, find keys for this
# and create containers that hold the values of those keys
# and repeate...
self._recurseSetAttr(self, attr, values)

Now, when generating the objects I can have a dict that looks something like this:
{"myContainer" : { "id" : 2, "foo" : { "id" : 3, "bar" : 1 } }}
that, once created, can be called like this:

In the scenario there's the
which tells the application what data type the object really is. This is referencing off of Django models but any python could apply.

All containers will have an
) key to them as part of their instantiation. This is mandatory.

The Rub

Ideally, we fill our the top level attributes and only when the user requests for the attributes that lie underneath it do we construct them based off the
value and the

So finally, let's say the
attribute has a foreign key field type. If we call
the app should understand that the 'newkey' attribute does not exist, query against our django instance, store the
attribute as the now more filled out Container, and return the
value that's been put to memory.

The Python Pitfall

I'd hoped it would be a simple
but Python seems to just use
with a default
(The recursion is real!). I've also had loads of trouble getting a
try: except:
to work.

As I write this I'm realizing how much more complicated it may be due to the recursive attribute setting relying on
Any suggestions would be greatly appreciated. - Cheers

Answer Source

So to answer the first part of the question: how to have __getattr__ call self.get() only when the attribute is not defined already. There are two attribute access methods in python classes: __getattribute__ and __getattr__. The first is called every time an attribute lookup is attempted, the second is called only when the normal attribute lookup system fails (including lookups in superclasses). Since you're defining __getattr__, which is only called when the attribute doesn't already exist, you can simply proxy it to a call to .get. Where you run into recursion issues is if you try to look up another attribute of self, that also doesn't yet exist, inside of __getattr__. The way to avoid this is to have a list of keys that require special handling and check if the current attribute requested is one of them. This typically is only needed when implementing __getattribute__.

Note that your .get method has a problem: attr and values are undefined. I'd give a slightly more concrete answer for what to put in __getattr__ if I knew what values for .get's attr and values want.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download