Tuesday, June 30, 2009

Python Attribute Descriptors

Python has a feature called Descriptors that allows your code to transform attributes when they are accessed. The code that uses your class can access attributes directly, but you can write methods that transform the value, so what is set is not what you get. This is more modularized that implementing __getattr__ and __setattr__, because the methods that end up being called are specific to the attribute. Because of this, you don't need a bunch of if statements that branch on the name of the attribute.

I wrote up a simple example. You can view the complete file on Zenoss Trac. Running the file runs a unit test.

An 'x' attribute is declared on MyClass. The value of 'x' will be accessed as a string, but it is declared to be a MyDescriptor.

class MyClass(object):
pass

for id in ['x']:
setattr(MyClass, id, MyDescriptor(id))

The MyDescriptor instance prepends 'bar_' to the value that is set on the 'x' attribute of a MyClass instance. This value is stored in a dictionary named '_values' on the MyClass instance.

def __set__(self, instance, value):
if not hasattr(instance, '_values'): instance._values = {}
instance._values[self.id] = 'bar_' + value

When the 'x' attribute is fetched from a MyClass Instance, the MyDescriptor instance prepends 'foo_' to the value it finds in '_values'.

def __get__(self, instance, owner):
if instance is None: return self
else: return 'foo_' + instance._values[self.id]

More information on descriptors is in the Python docs.