Ruby Impressions

David Abrahams david.abrahams at rcn.com
Sun Jan 13 00:06:43 EST 2002


"Paul Rubin" <phr-n2002a at nightsong.com> wrote in message
news:7xadvkcki2.fsf at ruckus.brouhaha.com...
> adamspitz at bigfoot.com (Adam Spitz) writes:
> > On that Wiki page, we tried to come up with a Python version of this,
> > and came pretty close,
>
> I think you could do something like it with python 2.2 metaclasses,

Absolutely, you can do it with metaclasses. You can even do it in Python
1.5, since the basic metaclass mechanism was available there, too (though in
a much cruder form).

> but it would confuse the hell out of people.

Aww, c'mon. Way too much has been made of how difficult metaclasses are to
understand. If the documentation wasn't so full of "Whoa, this is gonna bend
your mind" stuff, you could probably learn the material in half the time:

class auto_init_class(type):
    '''The (meta) type of all auto-initialized classes'''

    # Here's how we build new auto-initialized classes
    def __init__(self, name, bases, dict):
        # Let the superclass do most of the intialization
        super(auto_init_class,self).__init__(name, bases, dict)

        # if the user supplied initialize, but not __init__,
        # add a special __init__ function
        if dict.has_key('initialize') and not dict.has_key('__init__'):
            self.__init__ = initializer(dict['initialize'])

class initializer(object):
    '''initializer(function, target = None)

A function object class whose instances are used as __init__
functions in auto-initialized classes. The optional target argument is
the instance to which this function gets bound, if any'''

    # create, and bind the target if any
    def __init__(self, function, target = None):
        self.initialize = function
        self.target = target

    # How the function is invoked
    def __call__(self,*args):
        '''f.__call__(...); the call must have the same arity as the
initialization function'''

        # grab the names of the arguments to the initialize function
        argnames = self.initialize.func_code.co_varnames

        # bind the target if neccessary
        if self.target:
            args = (self.target,) + args

        # quick'n'dirty arity check. Should throw a real exception
        assert len(argnames) == len(args)

        # set all the attributes on the target object, skipping self
        for name,value in zip(argnames,args)[1:]:
            setattr(args[0],name,value)

        # now invoke the initialize function
        return apply(self.initialize, args)

    # The presence of a __get__ method makes initializer instances
    # into descriptors. See PEP 252 for an explanation.
    def __get__(self, obj, type=None):
        '''f.__get__(obj,type = None)

Used to cause class method-style binding of the 'self'  object.'''
        if obj == None: # retrieved directly from the class
            return self
        else:           # retrieved through an instance
            return initializer(self.initialize, obj) # a bound copy

# A simple base class which causes auto_initialization. Nothing to it.
class auto_init:
    __metaclass__ = auto_init_class

# A simple auto_init class
class Foo(auto_init):
    def initialize(self,a, b, c):
        pass

f = Foo(1,2,3)
print f.__dict__

# inheritance works too:
class Bar(Foo):
    def initialize(self, greet, whom):
        Foo.__init__(self, 4, 5, 6)

b = Bar('hello','world')
print b.__dict__






More information about the Python-list mailing list