Wednesday, April 26, 2006

How to work with Metaclasses

I just released another version of pywinauto - and I am quite happy with it. I even squeezed some more performance improvements (it is now approximately 10-20% faster then the previous release - though I think it will be waiting for the application itself to respond most of the time!).

One thing that I had wanted to make into this release was a change from using a function to return the correct control Wrapper class, to having a __metaclass__ attribute in the base class, and add functionality to it's __new__ method.

something like: (paraphrased - not run!)

class MetaWrapper(type):
registry = {}

# this is called when each class is defined
def __init__(cls, name, bases, attrs):
# add information defined in cls to the registry
# so that later we can return the correct class

def GetWrapper(handle):
# look in registry for correct class
return correct_class
GetWrapper = staticmethod(GetWrapper)

class HwndWrapper(object):
__metaclass__ = MetaWrapper

# called before each instance is created
def __new__(cls, handle):
correct_class = cls.GetWrapper(handle)

# need to call base __new__ ?
obj = correct_class.__new__(correct_class)

# as we are returning a different class from HwndWrapper we need to
# initialize it
return obj

def __init__(self, handle):
# rest of stuff here

But I was getting errors I could not explain! Now that I write that out - there were times when I was returning a HwndWrapper instance, would that mean that it was getting initialized twice? I don't think that would have caused the problems I was seeing (but then again - I can't think of anything else either!)

Maybe one of you out there who knows this magic better can set me on the right track. Is this even something that I should be using - or are meta classes too much magic most of the time?

Anyway - happy pythoneering :-)


  1. I don't think your "__new__" method is in the right place. I believe that "__new__" is only relevant to the metaclass, so it should be moved to MetaWrapper.

    See this article for more info:

    (Sorry for the long url).


  2. Ok, I was a little off. __new__ is not only for metaclasses, but, in your case, I think it should be moved to the metaclass.

    Sorry for the confusion.


  3. Ok - I figured it out - it wasn't a bug related to metaclasses at all - it was due to a logic error in the code. I wasted a bit of time fiddling with that - and wasted more time then I should have because I thought it was metaclass related.

    Thanks for the comments!