need something like __init__ in classes __classinit__

Kevin Jacobs jacobs at darwin.epbi.cwru.edu
Fri Mar 8 07:36:51 EST 2002


Jens Gelhaar <cuiod-tec at web.de> wrote:
> class metaclass(type):
>    def __new__(cls):
>        cls.__classinit__(...)

> class primary(Persistent):
>    __metaclass__=metaclass
>    def __classinit__(cls):
>      << do something ...>

> I get a error message, that I can not inherit from to different
> metaclasses.

I'm not sure my advise will help with ExtensionClasses, since I don't know
enough about how it is implemented.  Here is how I think it can be done
without changing the Python core:

  class ClassInit(type):
    def __new__(cls, name, bases, cls_dict):
      # Transform __classinit__ into a static method
      class_init = cls_dict.get('__classinit__',None)
      if class_init:
        cls_dict['__classinit__'] = staticmethod(class_init)

      # Call super instead of type.__new__ to allow cooperative metaclasses
      return super(ClassInit,cls).__new__(cls, name, bases, cls_dict)

    def __init__(self, name, bases, cls_dict):
      # Call __classinit__ if it exists and is callable.
      # This cannot be done in __new__ since the class may not be completely
      # initialized by its metaclasses.
      if callable(getattr(self, '__classinit__',None)):
        self.__classinit__(self)

  class ClassInitObject(object):
    __metaclass__ = ClassInit

  class Persistent:
    pass

  class primary(ClassInitObject,Persistent):
    def __classinit__(cls):
      print "In __classinit__:",cls
    def __init__(self):
      print "In __init__:",self

  p=primary()

The output is then:
  In __classinit__: <class '__main__.primary'>
  In __init__: <__main__.primary object at 0x812961c>

So the trick is to use multiple inheritance to allow multiple metaclasses. 
I have no idea if this will work with an ExtensionClass, but its much more
likely than getting Guido to support __initclass__ in the Python core.
(Anyway, this will certainly work with the ZODB from Zope3.)

I'll add this trick to my growing list of metaclass hacks.  If anyone is
interested, I'll post them on my website later today.  Here is the current
list of magic metaclasses:

  o A metaclass that lazily instantiates slots so that you can write:

    class A(object):
      __metaclass__ = LazySlotBuilder
      __slots__ = ('a')

    class B(object):
      __metaclass__ = LazySlotBuilder
      __slots__ = ('b')

    class AB(A,B):
      pass

    Without the metaclass magic, this would will result in a layout
    conflict: TypeError: multiple bases have instance lay-out conflict

  o A metaclass that adds constraint checking to attributes and slots.  This
    can be used to implement type checking in Python:

      class A(object):
       __metaclass__ = ConstrainedObject
       __slots__ = {'a':int,'b':float}

      class B(A):
        __attrs__ = {'c':str}

      foo=B()

      foo.a = foo.b = foo.c = 1

      assert foo.a == 1
      assert foo.b == 1.0
      assert foo.c == "1"
      assert type(foo.a) == int
      assert type(foo.b) == float
      assert type(foo.c) == str

      foo.a = 'Hello'  # Throws a ValueError from trying to run int('Hello')

  o A tuple building metaclass that allows access to tuple elements by name
    as well as by index.  There is also a version that allows access to
    elements by name in a case-insensitive manner.  These objects are very
    useful as a replacement for DB-API row tuples, which is why I call them
    Row objects:

      ABCRow=RowMetaClass(['a','b','c'])
      r=ABCRow( (1,2,3) )

      assert r['a']==r[0]==r.a==1
      assert r['b']==r[1]==r.b==2
      assert r['c']==r[2]==r.c==3

      ABCRow=InsensitiveRowMetaClass(['a','b','c'])
      r=ABCRow( (1,2,3) )

      assert r['a']==r['A']==r[0]==r.A==r.a==1
      assert r['b']==r['B']==r[1]==r.B==r.b==2
      assert r['c']==r['C']==r[2]==r.C==r.c==3
 
  o The above ClassInit metaclass.

Regards,
-Kevin

--
Kevin Jacobs
The OPAL Group - Enterprise Systems Architect
Voice: (216) 986-0710 x 19         E-mail: jacobs at theopalgroup.com
Fax:   (216) 986-0714              WWW:    http://www.theopalgroup.com



More information about the Python-list mailing list