How to pass arguments to the metaclass from the class definition?

metaclass, metaprogramming, python, python-2.x

Solution

Yes, there's an easy way to do it. In the metaclass's `__new__()` method just check in the class dictionary passed as the last argument. Anything defined in the `class` statement will be there. For example:

class MyMetaClass(type):
    def __new__(cls, class_name, parents, attrs):
        if 'meta_args' in attrs:
            meta_args = attrs['meta_args']
            attrs['args'] = meta_args[0]
            attrs['to'] = meta_args[1]
            attrs['eggs'] = meta_args[2]
            del attrs['meta_args'] # clean up
        return type.__new__(cls, class_name, parents, attrs)

class MyClass(object):
    __metaclass__ = MyMetaClass
    meta_args = ['spam', 'and', 'eggs']

myobject = MyClass()

from pprint import pprint
pprint(dir(myobject))
print myobject.args, myobject.to, myobject.eggs

Output:

['__class__',
 '__delattr__',
 '__dict__',
 '__doc__',
 '__format__',
 '__getattribute__',
 '__hash__',
 '__init__',
 '__metaclass__',
 '__module__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'args',
 'eggs',
 'to']
spam and eggs

Update

The code above will only work in Python 2 because syntax for specifying a metaclass was changed in an incompatible way in Python 3.

To make it work in Python 3 (but no longer in Python 2) is super simple to do and only requires changing the definition of `MyClass` to:

class MyClass(metaclass=MyMetaClass):
    meta_args = ['spam', 'and', 'eggs']

It's also possible to workaround the syntax differences and produce code that works in both Python 2 and 3 by creating base classes "on-the-fly" which involves explicitly invoking the metaclass and using the class that is created as the base class of the one being defined.

class MyClass(MyMetaClass("NewBaseClass", (object,), {})):
    meta_args = ['spam', 'and', 'eggs']

Class construction in Python 3 has also been modified and support was added that allows other ways of passing arguments, and in some cases using them might be easier than the technique shown here. It all depends on what you're trying to accomplish.

See @John Crawford's detailed answer for a description of of the process in the new versions of Python.

Problem

I'm trying to dynamically generate classes in python 2.7, and am wondering if you can easily pass arguments to the metaclass from the class object. I've read this post, which is awesome, but doesn't quite answer the question. at the moment I am doing: ``` def class_factory(args, to, meta_class): Class MyMetaClass(type): def __new__(cls, class_name, parents, attrs): attrs['args'] = args attrs['to'] = to attrs['eggs'] = meta_class class MyClass(object): metaclass = MyMetaClass ... ``` but this requires me to do the following ``` MyClassClass = class_factory('spam', 'and', 'eggs') my_instance = MyClassClass() ``` Is there a cleaner way of doing this?

Original source

Related problems