metaclasses -- aka Don Beaudry hook/hack (was RE: Why should i use python if i can use java)

James_Althoff at i2.com James_Althoff at i2.com
Mon Jun 11 18:44:57 EDT 2001


Geoff Gerrietts wrote:
>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).
<snip>

Geoff, Thanks very much for your response (and all the effort you put into
creating the examples and pointers to documentation!).

Actually, I am familiar with metaclasses -- aka the Don Beaudry hook.  What
I have found is that "the hook" doesn't seem to be complete in the sense
that only the "class" statement seems to know about the hook.  Other parts
of Python don't (unless I'm missing something).  So if you use a metaclass
to make a class "MyClass", its "type" will not match types.ClassType, for
example.  Also problematic is that "isinstance" will not accept MyClass as
an argument so you can't make an instance of MyClass and then test to see
that it (the instance) is an instance of MyClass (without adding some extra
contortions).

So it seems to me that "class-like instances" created using the Don Beaudry
hook are not really "first-class" class objects (Python sees them as
"instances" not as "class objects") so the "hook" is indeed a bit of a
"hack" at this point (but is very clever, nonetheless :-)).  And when using
metaclasses you have to be aware of the parts of Python that operate on
true "classes" -- and that fail on "instances" -- and keep extra machinery
around to deal with this -- which is a bit of a hassle and makes
metaclasses not as useful as they might otherwise be.

Hopefully the new class/type healing will fix all of this.

Jim

"Not Really a Class" Metaclass Example:

This example is an experiment to see if Don Beaudry-style metaclasses could
be used to implement Smalltalk-style metaclasses in Python.  It is
different than the metaclass examples shown in the docs in that it doesn't
use a fixed, pre-defined, secondary helper class to create the instances.
Instead it makes the helper class on the fly using "new.classobj" and saves
it as a metaclass instance field.

##############

import new,types

class MetaClass:

    def __init__(self,name,baseList,nameSpaceDict,isMetaClass=0):
        self.name = name
        self.baseList = baseList
        self.nameSpaceDict = nameSpaceDict
        self.isMetaClass = isMetaClass
        tempBaseList = []
        for base in baseList:
            if type(base) == types.ClassType:
                tempBaseList.append(base)
            else:
                if base != baseList[0]:
                    tempBaseList.append(base.classObject)
        self.classObject =
new.classobj(name,tuple(tempBaseList),nameSpaceDict)

    def __call__(self,*pargs,**kargs):
        instance = self.classObject(*pargs,**kargs)
        return instance

    def __getattr__(self,attr):
        if not self.isMetaClass:
            attr = self.classObject.__getattr__(attr)
            return attr
        raise AttributeError


Meta = MetaClass('Meta',(),{},isMetaClass=1)


class MyClass1MetaClass(MetaClass):

    def whoami(self):
        print 'MyClass1MetaClass.whoami:', self


MyClass1Meta = MyClass1MetaClass('Meta',(),{},isMetaClass=1)


class MyClass1(MyClass1Meta):

    def whoami(self):
        print 'MyClass1.whoami:', self


class MyClass2MetaClass(MyClass1MetaClass):

    def whoami(self):
        print 'MyClass2MetaClass.whoami:', self
        print 'and also:'
        MyClass1MetaClass.whoami(self)


MyClass2Meta = MyClass2MetaClass('Meta',(),{},isMetaClass=1)


class MyClass2(MyClass2Meta,MyClass1):

    def whoami(self):
        MyClass1.classObject.whoami(self)
        print 'and also:'
        print 'MyClass2.whoami:', self


print
MyClass1.whoami()
mc1 = MyClass1()
print
mc1.whoami()
print
MyClass2.whoami()
mc2 = MyClass2()
print
mc2.whoami()

print
print 'really a class?', type(MyClass1) == types.ClassType
print 'so what am I really?', type(MyClass1)

print
print 'extra contortions needed:'
print 'isinstance:', isinstance(mc2,MyClass2.classObject)
print 'isinstance:', isinstance(mc2,MyClass1.classObject)

print
print 'isinstance fails here:'
print 'isinstance:', isinstance(mc2,MyClass2)
print 'isinstance:', isinstance(mc2,MyClass1)

>>> reload(meta9)

MyClass1MetaClass.whoami: <meta9.MyClass1MetaClass instance at 00817DCC>

MyClass1.whoami: <meta9.MyClass1 instance at 0081546C>

MyClass2MetaClass.whoami: <meta9.MyClass2MetaClass instance at 0081478C>
and also:
MyClass1MetaClass.whoami: <meta9.MyClass2MetaClass instance at 0081478C>

MyClass1.whoami: <meta9.MyClass2 instance at 0081067C>
and also:
MyClass2.whoami: <meta9.MyClass2 instance at 0081067C>

really a class? 0
so what am I really? <type 'instance'>

extra contortions needed:
isinstance: 1
isinstance: 1

isinstance fails here:
isinstance:
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "c:\_dev\python20\meta9.py", line 85, in ?
    print 'isinstance:', isinstance(mc2,MyClass2)
TypeError: second argument must be a class
>>>






More information about the Python-list mailing list