Using metaclassed to dynamically generate a class based on a parameter to the objects init function.

Carl Banks invalidemail at aerojockey.com
Fri Jun 23 10:03:55 EDT 2006


sashang at gmail.com wrote:
> Hi
>
> I'd like to use metaclasses to dynamically generate a class based on a
> parameter to the objects init function.
>
> For example:
>
> class MetaThing(type):
>     def __init__(cls, name, bases, dict, extra_information):
>         super(MetaThing, cls).__init__(name, bases, dict)
>         #setup the class based on the parameter extra_information
>
> class Thing:
>     __metaclass__ = MetaThing
>     def __init__(self, extra_information):
>          #Somehow pass extra_information to the MetaThing
>
> extra_information = 1
> t = Thing(extra_information)

Tricky.  First of all, __init__ belongs to the class, not the object.
(Sometimes it's convenient to say it's the object's __init__ method,
but when mucking around with metaclasses it's important be precise
about what belongs to who, otherwise everyone gets confused.)  Because
__init__ belongs to the class, the object's class must already exist
before calling it, which is contrary to what you seem to want to do.

It seem as if, when creating an object, you want to create it's very
own class to go with it.  I suppose there could be use case for it, but
I highly recommend you consider whether features like instance methods
or classmethods can accomplish what you want.  If you'd still rather
that a Thing have its very own class, I recommend you forget about
metaclasses and use a factory function with a closure:

    def create_thing(extra_information):
        class Thing(object):
            def __init__(self):
                # use extra_information here
            ...
        return Thing()

If you don't like this, or if you insist on using a metaclass, and you
don't care that you'll be confusing the hell out of anyone reading the
code, the answer is to override the class's __new__ method.  Unlike
__int__, the __new__ method can return any object it wants, including
an object of a different class.  The class should subclass itself in
its __new__ method, providing the passed extra_information to the
constructor of the new subclass, then return an object created from
that subclass.  The subclass should override __new__ so as not to
repeat the hijinks of the class's __new__.

Don't follow?  The actual source code won't be much easier.  Here's an
example.

    class MetaThing(type):
        def __new__(metacls,name,bases,clsdict,extra_information):
            # use extra_information
            return type.__new__(metacls,name,bases,clsdict)

    class Thing(object):
         def __new__(cls,extra_information):
             clsdict = {'__new__':object.__new__}
             my_very_own_class = MetaThing(
                 "Subthing",(Thing,),clsdict,extra_information)
             return object.__new__(my_very_own_class)

Note that Thing doesn't and shouldn't define __metaclass__, since it
creates its subclass directly from the metaclass's constructor.  You
could, of course, also use the closure method I demonstrated above in
Thing's __new__ method--essentially you'd be using Thing's __new__
method as the factory function.


> The above sample won't work but I hope it demonstrates what I'm trying
> to do.

Again, I highly recommend you consider whether what you're "trying to
do" can be done more easily with instance or class methods.


Carl Banks




More information about the Python-list mailing list