I l@ve RuBoard Previous Section Next Section

5.6 Implementing Properties

Credit: Luther Blissett

5.6.1 Problem

You want client code to use normal attribute-access syntax for using, binding, or deleting instance attributes, but you want the semantics of these actions to be determined by method calls (e.g., to compute an attribute's value on the fly).

5.6.2 Solution

With Python 2.2 new-style classes, the new built-in property function lets you do this directly:

class Rectangle(object):
    def _ _init_ _(self, width, height):
        self.width = width
        self.height = height
    def getArea(self): return self.width * self.height
    def setArea(self, value): raise AttributeError, "Can't set 'area' attribute"
    area = property(getArea, setArea)

With classic classes, you must implement properties yourself with the special methods _ _getattr_ _ and _ _setattr_ _:

class Rectangle:
    def _ _init_ _(self, width, height):
        self.width = width
        self.height = height
    def getArea(self): return self.width * self.height
    def setArea(self, value): raise AttributeError, "Can't set 'area' attribute"
    def _ _getattr_ _(self, name):
        if name=='area': return self.getArea(  )
        raise AttributeError, name
    def _ _setattr_ _(self, name, value):
        if name=='area': return self.setArea(value)
        self._ _dict_ _[name] = value

5.6.3 Discussion

Properties are an important object-oriented concept. Instances of a class often need to expose two different kinds of attributes: those that hold data and those that are computed on the fly with a suitable method, whenever their values are required. If you expose the real attributes directly and the computed attributes via methods, such as getArea, current implementation issues will appear in the interface for your class and throughout the client code, which should really be independent from such issues. And if you ever change the implementation, you are in serious trouble.

The alternative of exposing everything via so-called accessor methods is also far from satisfactory. In this case, the code for your class fills up with highly repetitive boilerplate code such as:

def getWidth(self): return self.width

Even worse, your client code is cluttered with more verbose and less-readable statements such as:

r.setHeight(r.getHeight(  )+1)

rather than more concise and readable statements such as:

r.height += 1

Moreover, the unnecessary calls to the accessor methods slow your code's operation.

Properties let you have your cake and eat it too. Client code accesses all attributes uniformly (e.g., r.width, r.area) without caring or needing to know which are real and which are computed on the fly. Your class just needs a way to ensure that when client code accesses a computed attribute, the right method is called, and its return value is taken as the attribute's value. For example:

>>> r = Rectangle(10, 20)
>>> print r.area
200

When client code accesses a real attribute, nothing special is needed.

With Python 2.2's new-style classes, you can use the built-in property function to define properties. You pass it the accessor functions for get and set operations, optionally followed by one to use for deletions (an optional fourth argument is the attribute's documentation string). You bind the return value to the name, in class scope, that you want the client code to use when accessing the property on class instances.

In classic classes, you can still have properties, but you need to implement them yourself. When any code accesses an attribute that doesn't exist for an object, Python calls the _ _getattr_ _ method for the class (if it exists) with the attribute's name as the argument. You just need to test for the names of the properties that you are implementing and delegate to the appropriate method, as shown in the second solution. Whenever an attribute is set on your object (whether the attribute exists or not), Python calls the _ _setattr_ _ method for the class (if it exists) with the attribute's name and the new value assigned to it as arguments. Since _ _setattr_ _ is called for all attribute settings, it must also deal with setting real attributes in the normal ways (as items in self._ _dict_ _). Also, other methods in classes that implement _ _setattr_ _ often set items in self._ _dict_ _ directly to avoid triggering _ _setattr_ _ needlessly.

5.6.4 See Also

Properties are currently underdocumented. There is a minimal description in Guido's essay describing the unification of types and classes (http://www.python.org/2.2/descrintro.html#property); additional minimal information is available from the online help system (help(property)). However, by the time you read this, the Language Reference will likely have been updated.

    I l@ve RuBoard Previous Section Next Section