Re: Python factory-like type instances

Nicolas: Your metaclass example is a good example of when not to use metaclasses. I wouldn’t be surprised if it is executed slightly different to how you expect. Let’s look at how Foo is evaluated, starting with what’s written:

class Foo:
    __metaclass__ = FooMeta

This is equivalent to the following assignment:

Foo = FooMeta('Foo', (), {...})

As FooMeta has an __new__() method, the attempt to instantiate FooMeta will result in it being called. As the return value of __new__() is not a FooMeta instance, there is no attempt to call FooMeta.__init__(). So we could further simplify the code to:

Foo = {
    'linux2': LinuxFoo,
    'win32': WindowsFoo,
}.get(PLATFORM, None)
if not Foo:
    # XXX: this should _really_ raise something other than Exception
    raise Exception, 'Platform not supported'

So the factory function is gone completely here, and it is clear that the decision about which class to use is being made at module import time rather than class instantiation time.

Now this isn’t to say that metaclasses are useless. In both implementations, the code responsible for selecting the class has knowledge of all implementations. To add a new implementation (e.g. for Solaris or MacOS X), the factory function needs to be updated. A better solution would be to provide a way for new implementations to register themselves with the factory. A metaclass could be used to make the registration automatic:

class FooMeta(type):
    def __init__(self, name, bases, attrs):
        cls = super(FooMeta, self).__init__(name, bases, attrs)
        if cls.platform is not None:
            register_foo_implementation(klass.platform, cls)
        return cls

class Foo:
    __metaclass__ = FooMeta
    platform = None
    ...

class LinuxFoo(Foo):
    platform = 'linux2'

Now the simple act of defining a SolarisFoo class would be enough to have it registered and ready to use.

This Post Has 6 Comments

  1. Snark

    Nice post!

    I thought python was saner than C++ — you prove me wrong!

  2. James Henstridge

    John: that guy really should read up on Zope interfaces a bit more — they handle a lot of the things he is doing.

    Snark: if you think metaclasses are obscure, you should see what people do with sys._getframe().

  3. john Stowers

    @James,

    Yeah his first paragraph says as much. Still, I applaud his determination for going to the effort. A lesser capable plugin system that doesnt bring in zope.interface might be useful for those people who care about such things

  4. James Henstridge

    There isn’t much point in reinventing zope.interface. With the effort the Zope 3 developers have put in to modularise everything, using zope.interface does not imply pulling in the rest of Zope. There doesn’t seem to be much reason not to use it where appropriate.

Leave a Reply