metaclasses and setting __class__

scott cotton scott at chronis.pobox.com
Thu Jun 3 03:15:49 CEST 1999


On Wed, Jun 02, 1999 at 05:05:01PM -0500, Gordon McMillan wrote:
| scott cotton writes:
| 
| > Last night i was playing with meta classes, and found out
| > that we are not able to assign an instance's __class__
| > attribute to a metaclass instance.  was just wondering if
| > this is probably always going to be the case, or if there
| > are any plans to make .__class__ assignable to such beasts.
| 
| It wouldn't do any good. By the time you have an instance, it's too 
| late to add metaclass behaviors. All the magic happens at the time 
| the "class MyClass(MyMetaClassInstance):" is encountered. At this 
| point, MyMetaClassInstance can do all kinds of evil things to the way 
| MyClass works.

It seems like all this could happen when __class__ is
assigned to as well, provided the underlying mechanism knew
what to do -- seems like somehow checking interface rather
than type for that assignment would do the trick, but all
that is conjecture - i don't even know if it's possible - or
desirable but it did seem intuitive.

| Assigning to an instance's __class__ will alter the instance's 
| behavior and appearance, but not to the same extent that using a 
| metaclass will. 

challenge: make a metaclass that mucks with it's own class
hierarchy at run time on a per-instance basis.  for example:

class A(Meta): pass
class B(A): pass
class C(A): pass

have instances of A become instances of C on instantiation,
and instances of B become instances of C with B replaced for
A in all the __bases__ (and all __bases__ of all __bases__
...) of C.   Never mind why it might be considered useful,
or even worse why it could actually make things more clear. 

I think I found that metaclasses are the wrong tool for
this, but only after hours of playing with the madness and
nearing a state of raving lunacy. at one point early in the
game, the following:

class MyRealClass(MetaClassInstance): pass
print MyRealClass, type(MyRealClass)

yielded "__getitem__ <'string'>".

I swear - and no I didn't redefine the builtin 'type'.

needless to say, i gave up after a while, and used the
following function instead:

def rebuild(leaf_class, oldbase, newbase):
    if leaf_class == oldbase:
        return newbase
    class foo: pass
    oldbases = leaf_class.__bases__
    newbases = []
    for base in oldbases:
        newbases.append(rebuild(base, oldbase, newbase))
    foo.__bases__ = tuple(newbases)
    for k, v in leaf_class.__dict__.items():
        if k != '__bases__':
            foo.__dict__[k] = v
    return foo

It was used sortof like this:

class A:

    def __init__(self):
        if self.__class__ != A:
            self.__class__ = rebuild(C, A, B)
	else:
            self.__class__ = C
class B(A):

      def extrastuff(self): ...

class C(A): 
      
      def other_type_of_extrastuff(self): ...

scott





More information about the Python-list mailing list