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...