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
[email protected]                                           Hudson, MA 01749
                 ...So much code, so little time...