X-Archive: People/bwarsaw@cnri.reston.va.us
Received: from localhost by zippy.boston.sgi.com via SMTP (950413.SGI.8.6.12/911001.SGI)
	 id LAA01404; Wed, 19 Feb 1997 11:11:41 -0500
Message-Id: <199702191611.LAA01404@zippy.boston.sgi.com>
To: "Barry A. Warsaw" <bwarsaw@CNRI.Reston.VA.US>
References: <9702190411.AA27952@wheat> <199702190635.BAA00194@anthem.CNRI.Reston.Va.US> <199702190706.CAA04868@zippy.boston.sgi.com> <199702190722.CAA00252@anthem.CNRI.Reston.Va.US>
Subject: Re: Protocols and Interfaces (was Re: Protocols) 
Reply-to: donb@sgi.com
Date: Wed, 19 Feb 1997 11:11:41 -0500
From: Donald Beaudry <donb>

> Yes, please do.  I've been meaning to look into MESS and JF's
> ExtensionClasses stuff, so I'm definitely interested in hearing about
> other meta-class extensions.

[warning, this gets thick]

The idea that every class is itself an instance of a (meta-)class.
So, when a new class gets instantiated by invoking Python's 'class'
statement, the __init__ method defined in its meta-class gets run on
it.  

To apply this to the "protocol" problem, imagine that you had a
meta-class that could only instantiate classes that had a well know
list of methods.  Its __init__ method would look like this:

    class protocol_class:
        required_methods = ['spam', 'eggs']
        MissingMethod = "MissingMethod"
        def __init__(self):
            #note self is a class object
            for name in protocol_class.required_method:
                if not hasattr(self, name)
                    raise MissingMethod, name


So now, you want to write a class that implements the protocol.
You might think that writing:

    class my_protocol(protocol_class):
        def spam(): print 'spam'        
        def eggs(): print 'eggs'

would do the trick, but it wont.  This is making a new meta-class
by subclassing an existing one.  What you really want to do
is to derive your class from an *instance* of the meta-class.
So,

    protocol_base = protocol()

creates an instance of the meta-class (with no methods or attributes).
Now you can write:

    class my_protocol(protocol_base):
        def spam(): print 'spam'        
        def eggs(): print 'eggs'


Now, once and only once when this class statement is executed, the
__init__ method defined in protocol_class will be run with my_protocol
as its only argument.  You know ASAP that your class does or doesn't
implement the protocol.

Now this whole business of creating the meta-class independantly of of
the class itself is a bit of a pain.  I've come up with a syntax that
I think works.

    import object

    class protocol_base(object.base):
        class __class__:
            MissingMethod = "MissingMethod"
            required_methods = ['spam', 'eggs']
			def __init__(self):
				#note self is a class object
				for name in protocol_class.required_method:
					if not hasattr(self, name)
						raise MissingMethod, name

This is based on the observation that

    class foo: pass
    f = foo()
    f.__class__ is foo

so what would you expect foo.__class__ to be?  In the current Python,
it's an error, unless, you nest another class definition inside of foo
as I have inside of my example protocol_base class.

The trick is that protocol_base is derived from object.base which is
an instance of the meta-class object.base_class (written in C).
Sub-classing an instance of a meta-class is really just another way of
creating a new instance of a meta-class.  In this case, the
object.base_class meta-class. When the object.base_class tries to
instantiate the protocol_base class, it notices that it contain a
__class__ attribute and since this __class__ attribute is a class
object that has no base classes, it assumes that it is a
specialization of itself and should be used as the class of
protocol_base!  (well I find it exciting ;).

Once the above class defination has been executed, anyone wishing to
implement the protocol can simply derive an implementation from the
protocol_base class.  Best of all, they don't even need to know a
thing about meta-classes.  But, they will get a nice little error
message if their class fails to live up to the protocol.

		--Don

