Meta Object Programming in Python This module provides hooks for overriding various aspects of the Python class system. In particular, it give you complete control over the instantiation and attribute access of class objects. With this module you can give your classes methods of their own. Here is a simple class with a method of its own: import object class foo(object.base): def __init__(self, a): self.a = a class __class__: def frob(self): print "I've been frobbed", self foo.frob() Let's take a close look at the example. Everything looks about as you would expect of any class definition until you hit the embedded class definition. All classes derived from object.base are instances of some meta-class. A meta-class is simply a class who's instances are themselves classes. An easy way to think about this is to say that a class describes an instance and meta-class describes class. Any class derived from object.base can specialize its own meta-class. These specialization are described in the embedded class named __class__. This follows from the idea that an instance has an __class__ attribute that tells you from which class it was instantiated. So, any class that is an instance of a meta-class should also support the __class__ attribute to tell you from which meta-class it was instantiated. When defining a class, you have the option of specializing the meta-class to suit the need of the class being defined. To do this you only need to embed a class definition for a class named __class__ inside the defination of your class. Any methods added to the embeded meta-class will be available for use as methods on the class object. In the example above, the foo class has method called frob. Note that this method is not available for use by instances of foo, but it can be used by classes derived from foo. From this example, class bar(foo): def __init__(self, a): self.a = a f = foo() b = bar() bar.frob() will work just fine, but f.frob() and b.frob() will raise an AttributeError. Once you accept the idea that classes too should be able to have methods of their own, you must understand that there is a potential ambiguity that must be avoided. Consider this example. import object class ham(base.object): def print(self): print self.ham_data class __class__: def print(self): print self.class_data In this example both the class and the instances of the the class have a print method defined. This means that h = ham() h.print() and ham.print() will both work. But, ham.print and h.print are not the same method. One knows how to print instances of ham, h.print, while the other knows how to print ham classes, ham.print. So far everything is fine, but quite often, a derived class will implement a method in terms of the same method as defined on a base class. It is not uncommon to see code like this: class spam(ham): def print(self, how_many): ham.print(self) # XXX wrong print self.spam_data What is expected here is that the unbound print method for ham instances will be called with self as it's first argument. But this is not what will happen. The ham class has a print method of it's own. That is method that will get called. To get the desired behavior of having ham's unbound *instance* print method called, you must make the call through ham's "unbound instance". class spam(ham): def print(self, how_many): ham._.print(self) print self.spam_data Every class derived from object.base has an "unbound instance" that can be accessed through the "_". This object acts just like an instance of the class when attribute access is applied to it. The only difference is that when a real instance is used to access a method, a bound method will be returned. Using the "unbound instance" to access a method always results in an unbound method. When specializing your meta-class you can define the following special methods. __call__(self, *args, **kw) Is invoked whenever the function call operator is applied to the class. It is expected to return a fully initialized instance of base or one of it's sub-classes. The default of __call__ does the following. def __call__(self, *args, **kw): i = apply(self.__new__, args, kw) try: init = i.__init__ except AttributeError: pass else: if kw: apply(init, args, kw) else: apply(init, args) return i __new__(self, *args, **kw) Is invoked as part of the default instantiation process of the class (see __call__). It is expected to return an uninitialized instance of the class indicated by 'self'. The arguments are the same as those that will be passed to __init__ method of the *instance*, but generally should be ignored. __init__(self, name, bases, dict) Is called on a newly created instances of the class. This includes classes derived from other instances of the class. The 'name' specifes the name of the new class, 'bases' is the list of bases classes as specified in the class statement, and 'dict' is the dictionary of methods and class variables as specifed in the lexical scope of the class. Changes to the dictionary *will* affect the class. The __init__ routine is a good place to play around with the defination of the class. __getattr__(self, name) Is invoked whenever the class does not have an attribute with the given name. It should return a value for the attribute or raise an AttributeError if it cannot. __setattr__(self, name, value) Is invoked whenever an attribute is about to be written to the class. __delattr__(self, name) Is invoked whenever an attribute is about to be removed from class. -- Donald Beaudry Silicon Graphics Compilers/MTI 1 Cabot Road donb@sgi.com Hudson, MA 01749 ...So much code, so little time...