Why should i use python if i can use java

Geoffrey Gerrietts geoff at homegain.com
Mon Jun 11 16:32:08 EDT 2001


James Althoff <James_Althoff at i2.com> writes:
> From: James_Althoff at i2.com [mailto:James_Althoff at i2.com]
> 
> Geoff Gerrietts <geoff at homegain.com> wrote:
> <snip>
> >In Python, I know how to write classes whose instances are
> >other classes.
> 
> Do you mean "whose instances are *themselves* classes"?  And if
> so, How do you do this?
> (Can you do this in *Python* -- without doing a C extension?)

Yes, I really meant that. :)

This is the "metaclass" facility -- you can get a much more
formal description of how that works from looking at those docs
(and playing with the examples).

To use metaclasses, you need a metaclass and a helper class. Your
code will instantiate the metaclass. Subclasses of the metaclass
will be operated on ('rewritten') by the helper. An example
appears below.

The metaclass feature is not generally useful. It can be useful
in certain very specific cases, especially if you're writing
toolkits -- you can build functionality into your
(meta)superclass that will "subvert" later declarations. 

I've appended an example to this message. It's heavily indebted
to the example used in Guido's paper on metaclasses, found at:

http://www.python.org/doc/essays/metaclasses

That's a good place for further reading -- in my experience, the
toughest part about metaclasses was figuring out what happened
where -- the sequence of calls and what role each of the players
played. I've tried to make that as clear as possible in this
example....

Thanks,
--G.

---
Geoff Gerrietts <geoff at homegain.com>
Software Engineer, HomeGain.com
510-655-0800 x4320
 

#!/usr/bin/python -i

print 
print "defining MetaClass"
class MetaClass:
    def __init__(self, name, bases, namespace):
        """ for creation of class-level instances """
        print "Called MetaClass.__init__ for %s" % name

    def __call__(self):
        """ for creation of instances of instances """
        print "Called MetaClass.__call__"
        return Helper(self)

print 
print "defining Helper"
class Helper:
    def __init__(self, meta):
        """ invoked from MetaClass.__call__ for inst of inst
        """
        self.meta = meta
        print "Called Helper.__init__ for ", meta

    def bungee(self, *args):
        print "Helper.bungee called"

# here we directly instantiate a class by passing it a name, a
# list of base classes, and a namespace.  we call
# MetaClass.__init__ just like we'd expect here
print 
print "defining MetaInst (instantiating MetaClass)"
MetaInst = MetaClass('MetaInst', (), {})

# here MetaClass.__init__ gets called again! note that the
# __init__ must accept name, bases, namespace or else this 
# will flop
print 
print "defining MyStuff"
class MyStuff (MetaInst):
    def myMethod(self, *args):
        print "MyStuff.myMethod called"

# the actual instance of our child class invokes the __call__
# routine on our MetaClass. The instance itself is the return
# value from that __call__.
print 
print "instantiating MyStuff"
mine = MyStuff()
print "Class of our instance ", mine.__class__

# now if you've been following me, you'll be thinking that our
# instance 'mine' is an instance of 'Helper' (the return value
# from MetaClass.__call__), so we can call mine.bungee and see
# what we expect.
print
print "calling bungee"
mine.bungee()

# here's the thing that kinda sucks -- unless you've saved the
# class information in MetaClass.__init__ (and you've overridden
# __getattr__ to get at that stored info), you've lost your
# subclass.
print
print "calling myMethod"
mine.mymethod()

# here's a better init:

class BetterMetaClass:
    def __init__(self, name, bases, namespace):
        self.__name__ = name
        self.__bases__ = bases
        self.__namespace__ = namespace
        # if you want to muck around with the namespace,
        # this might be a good place to do it.

    def __call__(self):
        return BetterHelper(self)

# and here's a Helper that uses it. note that this Helper
# won't look in its own namespace for matches!
class BetterHelper:
    def __init__(self, meta):
        self.__meta__ = meta

    def __getattr__(self, name):
        try:
            value = self.__meta__.__namespace__[name]
        except KeyError:
            raise AttributeError, name
        return value




More information about the Python-list mailing list