next up previous contents
Next: Calling a Class Up: New Class Features Previous: New Operator Overloading

Trapping Attribute Access

You can define three new ``magic'' methods in a class now: __getattr__(self, name), __setattr__(self, name, value) and __delattr__(self, name).

The __getattr__ method is called when an attribute access fails, i.e. when an attribute access would otherwise raise AttributeError --- this is after the instance's dictionary and its class hierarchy have been searched for the named attribute. Note that if this method attempts to access any undefined instance attribute it will be called recursively!

The __setattr__ and __delattr__ methods are called when assignment to, respectively deletion of an attribute are attempted. They are called instead of the normal action (which is to insert or delete the attribute in the instance dictionary). If either of these methods most set or delete any attribute, they can only do so by using the instance dictionary directly --- self.__dict__ --- else they would be called recursively.

For example, here's a near-universal ``Wrapper'' class that passes all its attribute accesses to another object. Note how the __init__ method inserts the wrapped object in self.__dict__ in order to avoid endless recursion (__setattr__ would call __getattr__ which would call itself recursively).

class Wrapper:
    def __init__(self, wrapped):
        self.__dict__['wrapped'] = wrapped
    def __getattr__(self, name):
        return getattr(self.wrapped, name)
    def __setattr__(self, name, value):
        setattr(self.wrapped, name, value)
    def __delattr__(self, name):
        delattr(self.wrapped, name)

import sys
f = Wrapper(sys.stdout)
f.write('hello world\n')          # prints 'hello world'

A simpler example of __getattr__ is an attribute that is computed each time (or the first time) it it accessed. For instance:

from math import pi

class Circle:
    def __init__(self, radius):
        self.radius = radius
    def __getattr__(self, name):
        if name == 'circumference':
            return 2 * pi * self.radius
        if name == 'diameter':
            return 2 * self.radius
        if name == 'area':
           return pi * pow(self.radius, 2)
        raise AttributeError, name



guido@cwi.nl