Monday, March 29, 2010

Python Derived Attributes

Python allows classes to define managed properties. Managed properties are just like regular attributes when used. The only difference between regular instance attributes and managed properties is that the latter lets the developer define behavior that is executed when an attribute is set or accessed.

There are countless uses for such a feature. For instance, when an attribute is set, some constraint on the allowed attribute values can be enforced. Another powerful use of managed properties is to create derived attributes. A derived attribute looks like a normal attribute to the world outside of the object. The difference being that derived attributes are computed from other, possibly derived, attributes of the object.

Since the value returned from these attributes are in fact derived from other attributes, there is no way to set the attribute value directly. In order to alter the outcome of accessing a derived attribute, you would have to alter another attribute that is used to compute the derived attribute.

This also introduces a level of indirection that can also be quite powerful in most cases. Developers using the object do not need to concern themselves with the correct output of accessing the derived attribute.

To define a derived attribute, all you need is a method that will compute the value to return. This method is then assigned to a managed property. The method should really only use other attributes of the same object to compute the returned value. Otherwise, it really wouldn't be a derived attribute, just a computed attribute.

Below is a very simplistic example of how to derive a full name form a first name and last name.
class Person(object):
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name

def get_full_name(self):
return "%s %s"%(self.first_name, self.last_name)

full_name = property(get_full_name)

if __name__ == "__main__":

person_obj = Person("John", "Smith")

print "Full Name:", person_obj.full_name