Metaclasses vs. standard Python reflection?

Dave Benjamin ramen at lackingtalent.com
Tue May 6 12:04:14 EDT 2003


In article <zFKta.48587$3M4.1298130 at news1.tin.it>, Alex Martelli wrote:
> It has little to do with subclassing.  A class is an INSTANCE of its
> metaclass -- the relation to subclasses is there, but tangential.

This I understand. I was just making the observation that defining
__metaclass__ within a class definition is a similar mechanism to defining
superclasses. It takes place in the class itself, in contrast to the idea of
externally applying "aspects" to a class. It's like saying "My metaclass
is..." instead of "My superclass is...". Is this accurate?

> You could track down the INSTANCES of Foo, or some of them, and
> reassign THEIR __class__.  But Foo's own __class__ is generally NOT
> reassignable.  If Foo.Foo wasn't yet instantiated of course you could
> easily rebind Foo.Foo = BarFoo, so FUTURE instantiations use the new
> class with Bar.Bar as its metaclass.

Okay, here's my attempt at illustrating this technique. I'll just take a
very simple class and rebind it within its module so that it appears to be
the same class but additionally logs the entry and exit of its methods:

Simple.py
---------

class Simple:
    def __init__(self, x):
        self.x = x

    def print_x(self):
        print 'x = %s' % self.x

Meta.py
-------

import Simple

def log_call(method):
    def new_method(self, *args, **kwds):
        print 'Entering %s...' % method
        result = method(self, *args, **kwds)
        print 'Exiting %s...' % method

    return new_method

class Logger(type):
    def __new__(cls, name, bases, dict):
        for key in dict:
            if callable(dict[key]):
                dict[key] = log_call(dict[key])
                    
        return type.__new__(cls, name, bases, dict)

Simple.Simple = Logger(Simple.Simple.__name__,
                       Simple.Simple.__bases__,
                       Simple.Simple.__dict__)

s = Simple.Simple(123)
s.print_x()

Output
------

$ python Meta.py
Entering <function __init__ at 0x816ab54>...
Exiting <function __init__ at 0x816ab54>...
Entering <function print_x at 0x816cfec>...
x = 123
Exiting <function print_x at 0x816cfec>...

Discussion
----------

On the surface, this technique seems to achieve the desired result. However,
I have a hunch this will not work properly with inheritance. For instance,
if another class extends the Simple class before I rebind it, I will have to
apply Logger to those classes as well.

How could I achieve this effect with the global __metaclass__? I've tried
doing things like:

__metaclass__ = Logger
Simple.__metaclass__ = Logger

to no effect. I think I'm still very confused about how the metaclass
mechanism works.

Thanks for the reply,
Dave




More information about the Python-list mailing list