7512

What aremetaclasses? What are they used for?

Mateen Ulhaq's user avatar
Mateen Ulhaq
27.7k21 gold badges120 silver badges155 bronze badges
askedSep 19, 2008 at 6:10
Bite code's user avatar
1
  • I see a class as ahuman-readable documentation of a given data-structure. It's time to create a class when (A) it is used in multiple modules and (B) it has a complex and/or non-obvious structure. Why make a Vec3D class when using a list [x,y,z] is so simple? But a Car object with wheels engine chassis etc is a different story. Dynamic class creation is seldom needed if you follow this philosophy. But Python is avery dynamic language and so it ispossible to do so. Modules are also dynamic in interesting but rarely-used ways.CommentedJan 13, 2024 at 7:00

26 Answers26

9294
+250

Classes are objects

Before delving into metaclasses, a solid grasp of Python classes is beneficial. Python holds a particularly distinctive concept of classes, a notion it adopts from the Smalltalk language.

In most languages, classes are descriptions of how to create an object. That is somewhat true in Python too:

>>> class ObjectCreator(object):...     pass>>> my_object = ObjectCreator()>>> print(my_object)    <__main__.ObjectCreator object at 0x8974f2c>

But classes are more than that in Python.Classes are objects too.

Yes, objects.

When a Python script runs, every line of code is executed from top to bottom. When the Python interpreter encounters theclass keyword, Python creates anobject out of the "description" of the class that follows. Thus, the following instruction

>>> class ObjectCreator(object):...     pass

...creates anobject with the nameObjectCreator!

This object (the class) is itself capable of creating objects (calledinstances).

But still, it's an object. Therefore, like all objects:

  • you can assign it to a variable1

    JustAnotherVariable = ObjectCreator
  • you can attach attributes to it

    ObjectCreator.class_attribute = 'foo'
  • you can pass it as a function parameter

    print(ObjectCreator)

1 Note that merely assigning it to another variable doesn't change the class's__name__, i.e.,

>>> print(JustAnotherVariable)    <class '__main__.ObjectCreator'>>>> print(JustAnotherVariable())    <__main__.ObjectCreator object at 0x8997b4c>

Creating classes dynamically

Since classes are objects, you can create them on the fly, like any object.

First, you can create a class in a function usingclass:

>>> def choose_class(name):...     if name == 'foo':...         class Foo(object):...             pass...         return Foo # return the class, not an instance...     else:...         class Bar(object):...             pass...         return Bar>>> MyClass = choose_class('foo')>>> print(MyClass) # the function returns a class, not an instance    <class '__main__.Foo'>>>> print(MyClass()) # you can create an object from this class    <__main__.Foo object at 0x89c6d4c>

But it's not so dynamic, since you still have to write the whole class yourself.

Since classes are objects, they must be generated by something.

When you use theclass keyword, Python creates this object automatically. But aswith most things in Python, it gives you a way to do it manually.

Remember the functiontype? The good old function that lets you know whattype an object is:

>>> print(type(1))    <class 'int'>>>> print(type("1"))    <class 'str'>>>> print(type(ObjectCreator))    <class 'type'>>>> print(type(ObjectCreator()))    <class '__main__.ObjectCreator'>

Well,type also has a completely different ability: it can create classes on the fly.type can take the description of a class as parameters,and return a class.

(I know, it's silly that the same function can have two completely different uses according to the parameters you pass to it. It's an issue due to backwardcompatibility in Python)

type works this way:

type(name, bases, attrs)

Where:

  • name: name of the class
  • bases: tuple of the parent class (for inheritance, can be empty)
  • attrs: dictionary containing attributes names and values

e.g.:

>>> class MyShinyClass(object):...     pass

can be created manually this way:

>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object>>> print(MyShinyClass)    <class '__main__.MyShinyClass'>>>> print(MyShinyClass()) # create an instance with the class    <__main__.MyShinyClass object at 0x8997cec>

You'll notice that we useMyShinyClass as the name of the classand as the variable to hold the class reference. They can be different,but there is no reason to complicate things.

type accepts a dictionary to define the attributes of the class. So:

>>> class Foo(object):...     bar = True

Can be translated to:

>>> Foo = type('Foo', (), {'bar':True})

And used as a normal class:

>>> print(Foo)    <class '__main__.Foo'>>>> print(Foo.bar)    True>>> f = Foo()>>> print(f)    <__main__.Foo object at 0x8a9b84c>>>> print(f.bar)    True

And of course, you can inherit from it, so:

>>> class FooChild(Foo):...     pass

would be:

>>> FooChild = type('FooChild', (Foo,), {})>>> print(FooChild)    <class '__main__.FooChild'>>>> print(FooChild.bar) # bar is inherited from Foo    True

Eventually, you'll want to add methods to your class. Just define a functionwith the proper signature and assign it as an attribute.

>>> def echo_bar(self):...     print(self.bar)>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})>>> hasattr(Foo, 'echo_bar')    False>>> hasattr(FooChild, 'echo_bar')    True>>> my_foo = FooChild()>>> my_foo.echo_bar()    True

And you can add even more methods after you dynamically create the class, just like adding methods to a normally created class object.

>>> def echo_bar_more(self):...     print('yet another method')>>> FooChild.echo_bar_more = echo_bar_more>>> hasattr(FooChild, 'echo_bar_more')    True

You see where we are going: in Python, classes are objects, and you can create a class on the fly, dynamically.

This is what Python does when you use the keywordclass, and it does so by using a metaclass.

What are metaclasses (finally)

Metaclasses are the 'stuff' that creates classes.

You define classes in order to create objects, right?

But we learned that Python classes are objects.

Well, metaclasses are what create these objects. They are the classes' classes,you can picture them this way:

MyClass = MetaClass()my_object = MyClass()

You've seen thattype lets you do something like this:

MyClass = type('MyClass', (), {})

It's because the functiontype is in fact a metaclass.type is themetaclass Python uses to create all classes behind the scenes.

Now you wonder "why the heck is it written in lowercase, and notType?"

Well, I guess it's a matter of consistency withstr, the class that createsstrings objects, andint the class that creates integer objects.type isjust the class that creates class objects.

You see that by checking the__class__ attribute.

Everything, and I mean everything, is an object in Python. That includes integers,strings, functions and classes. All of them are objects. And all of them havebeen created from a class:

>>> age = 35>>> age.__class__    <type 'int'>>>> name = 'bob'>>> name.__class__    <type 'str'>>>> def foo(): pass>>> foo.__class__    <type 'function'>>>> class Bar(object): pass>>> b = Bar()>>> b.__class__    <class '__main__.Bar'>

Now, what is the__class__ of any__class__ ?

>>> age.__class__.__class__    <type 'type'>>>> name.__class__.__class__    <type 'type'>>>> foo.__class__.__class__    <type 'type'>>>> b.__class__.__class__    <type 'type'>

So, a metaclass is just the stuff that creates class objects.

You can call it a 'class factory' if you wish.

type is the built-in metaclass Python uses, but of course, you can create yourown metaclass.

The__metaclass__ attribute

In Python 2, you can add a__metaclass__ attribute when you write a class (see next section for the Python 3 syntax):

class Foo(object):    __metaclass__ = something...    [...]

If you do so, Python will use the metaclass to create the classFoo.

Careful, it's tricky.

You writeclass Foo(object) first, but the class objectFoo is not createdin memory yet.

Python will look for__metaclass__ in the class definition. If it finds it,it will use it to create the class objectFoo. If it doesn't, it will usetype to create the class.

Read that several times.

When you do:

class Foo(Bar):    pass

Python does the following:

Is there a__metaclass__ attribute inFoo?

If yes, create in-memory a class object (I said a class object, stay with me here), with the nameFoo by using what is in__metaclass__.

If Python can't find__metaclass__, it will look for a__metaclass__ at the MODULE level, and try to do the same (but only for classes that don't inherit anything, basically old-style classes).

Then if it can't find any__metaclass__ at all, it will use theBar's (the first parent) own metaclass (which might be the defaulttype) to create the class object.

Be careful here that the__metaclass__ attribute will not be inherited, the metaclass of the parent (Bar.__class__) will be. IfBar used a__metaclass__ attribute that createdBar withtype() (and nottype.__new__()), the subclasses will not inherit that behavior.

Now the big question is, what can you put in__metaclass__?

The answer is something that can create a class.

And what can create a class?type, or anything that subclasses or uses it.

Metaclasses in Python 3

The syntax to set the metaclass has been changed in Python 3:

class Foo(object, metaclass=something):    ...

i.e. the__metaclass__ attribute is no longer used, in favor of a keyword argument in the list of base classes.

The behavior of metaclasses however stayslargely the same.

One thing added to metaclasses in Python 3 is that you can also pass attributes as keyword-arguments into a metaclass, like so:

class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):    ...

Read the section below for how Python handles this.

Custom metaclasses

The main purpose of a metaclass is to change the class automatically,when it's created.

You usually do this for APIs, where you want to create classes matching thecurrent context.

Imagine a stupid example, where you decide that all classes in your moduleshould have their attributes written in uppercase. There are several ways todo this, but one way is to set__metaclass__ at the module level.

This way, all classes of this module will be created using this metaclass,and we just have to tell the metaclass to turn all attributes to uppercase.

Luckily,__metaclass__ can actually be any callable, it doesn't need to be aformal class (I know, something with 'class' in its name doesn't need to bea class, go figure... but it's helpful).

So we will start with a simple example, by using a function.

# the metaclass will automatically get passed the same argument# that you usually pass to `type`def upper_attr(future_class_name, future_class_parents, future_class_attrs):    """      Return a class object, with the list of its attribute turned      into uppercase.    """    # pick up any attribute that doesn't start with '__' and uppercase it    uppercase_attrs = {        attr if attr.startswith("__") else attr.upper(): v        for attr, v in future_class_attrs.items()    }    # let `type` do the class creation    return type(future_class_name, future_class_parents, uppercase_attrs)__metaclass__ = upper_attr # this will affect all classes in the moduleclass Foo(): # global __metaclass__ won't work with "object" though    # but we can define __metaclass__ here instead to affect only this class    # and this will work with "object" children    bar = 'bip'

Let's check:

>>> hasattr(Foo, 'bar')    False>>> hasattr(Foo, 'BAR')    True>>> Foo.BAR    'bip'

Now, let's do exactly the same, but using a real class for a metaclass:

# remember that `type` is actually a class like `str` and `int`# so you can inherit from itclass UpperAttrMetaclass(type):    # __new__ is the method called before __init__    # it's the method that creates the object and returns it    # while __init__ just initializes the object passed as parameter    # you rarely use __new__, except when you want to control how the object    # is created.    # here the created object is the class, and we want to customize it    # so we override __new__    # you can do some stuff in __init__ too if you wish    # some advanced use involves overriding __call__ as well, but we won't    # see this    def __new__(        upperattr_metaclass,        future_class_name,        future_class_parents,        future_class_attrs    ):        uppercase_attrs = {            attr if attr.startswith("__") else attr.upper(): v            for attr, v in future_class_attrs.items()        }        return type(future_class_name, future_class_parents, uppercase_attrs)

Let's rewrite the above, but with shorter and more realistic variable names now that we know what they mean:

class UpperAttrMetaclass(type):    def __new__(cls, clsname, bases, attrs):        uppercase_attrs = {            attr if attr.startswith("__") else attr.upper(): v            for attr, v in attrs.items()        }        return type(clsname, bases, uppercase_attrs)

You may have noticed the extra argumentcls. There isnothing special about it:__new__ always receives the class it's defined in, as the first parameter. Just like you haveself for ordinary methods which receive the instance as the first parameter, or the defining class for class methods.

But this is not proper OOP. We are callingtype directly and we aren't overriding or calling the parent's__new__. Let's do that instead:

class UpperAttrMetaclass(type):    def __new__(cls, clsname, bases, attrs):        uppercase_attrs = {            attr if attr.startswith("__") else attr.upper(): v            for attr, v in attrs.items()        }        return type.__new__(cls, clsname, bases, uppercase_attrs)

We can make it even cleaner by usingsuper, which will ease inheritance (because yes, you can have metaclasses, inheriting from metaclasses, inheriting from type):

class UpperAttrMetaclass(type):    def __new__(cls, clsname, bases, attrs):        uppercase_attrs = {            attr if attr.startswith("__") else attr.upper(): v            for attr, v in attrs.items()        }        # Python 2 requires passing arguments to super:        return super(UpperAttrMetaclass, cls).__new__(            cls, clsname, bases, uppercase_attrs)        # Python 3 can use no-arg super() which infers them:        return super().__new__(cls, clsname, bases, uppercase_attrs)

Oh, and in Python 3 if you do this call with keyword arguments, like this:

class Foo(object, metaclass=MyMetaclass, kwarg1=value1):    ...

It translates to this in the metaclass to use it:

class MyMetaclass(type):    def __new__(cls, clsname, bases, dct, kwarg1=default):        ...

That's it. There is really nothing more about metaclasses.

The reason behind the complexity of the code using metaclasses is not becauseof metaclasses, it's because you usually use metaclasses to do twisted stuffrelying on introspection, manipulating inheritance, vars such as__dict__, etc.

Indeed, metaclasses are especially useful to do black magic, and thereforecomplicated stuff. But by themselves, they are simple:

  • intercept a class creation
  • modify the class
  • return the modified class

Why would you use metaclasses classes instead of functions?

Since__metaclass__ can accept any callable, why would you use a classsince it's obviously more complicated?

There are several reasons to do so:

  • The intention is clear. When you readUpperAttrMetaclass(type), you knowwhat's going to follow
  • You can use OOP. Metaclass can inherit from metaclass, override parent methods. Metaclasses can even use metaclasses.
  • Subclasses of a class will be instances of its metaclass if you specified a metaclass-class, but not with a metaclass-function.
  • You can structure your code better. You never use metaclasses for something as trivial as the above example. It's usually for something complicated. Having the ability to make several methods and group them in one class is very useful to make the code easier to read.
  • You can hook on__new__,__init__ and__call__. Which will allow you to do different stuff, Even if usually you can do it all in__new__,some people are just more comfortable using__init__.
  • These are called metaclasses, damn it! It must mean something!

Why would you use metaclasses?

Now the big question. Why would you use some obscure error-prone feature?

Well, usually you don't:

Metaclasses are deeper magic that99% of users should never worry about it.If you wonder whether you need them,you don't (the people who actuallyneed them know with certainty thatthey need them, and don't need anexplanation about why).

Python Guru Tim Peters

The main use case for a metaclass is creating an API. A typical example of this is the Django ORM. It allows you to define something like this:

class Person(models.Model):    name = models.CharField(max_length=30)    age = models.IntegerField()

But if you do this:

person = Person(name='bob', age='35')print(person.age)

It won't return anIntegerField object. It will return anint, and can even take it directly from the database.

This is possible becausemodels.Model defines__metaclass__ andit uses some magic that will turn thePerson you just defined with simple statementsinto a complex hook to a database field.

Django makes something complex look simple by exposing a simple APIand using metaclasses, recreating code from this API to do the real jobbehind the scenes.

The last word

First, you know that classes are objects that can create instances.

Well, in fact, classes are themselves instances. Of metaclasses.

>>> class Foo(object): pass>>> id(Foo)    142630324

Everything is an object in Python, and they are all either instance of classesor instances of metaclasses.

Except fortype.

type is actually its own metaclass. This is not something you couldreproduce in pure Python, and is done by cheating a little bit at the implementationlevel.

Secondly, metaclasses are complicated. You may not want to use them forvery simple class alterations. You can change classes by using two different techniques:

99% of the time you need class alteration, you are better off using these.

But 98% of the time, you don't need class alteration at all.

Sign up to request clarification or add additional context in comments.

26 Comments

It appears that in Djangomodels.Model it does not use__metaclass__ but ratherclass Model(metaclass=ModelBase): to reference aModelBase class which then does the aforementioned metaclass magic. Great post! Here's the Django source:github.com/django/django/blob/master/django/db/models/…
<<Be careful here that the__metaclass__ attribute will not be inherited, the metaclass of the parent (Bar.__class__) will be. IfBar used a__metaclass__ attribute that createdBar withtype() (and nottype.__new__()), the subclasses will not inherit that behavior.>> -- Could you/someone please explain a bit deeper this passage?
@MaxGoodridge That's the Python 3 syntax for metaclasses. SeePython 3.6 Data model VSPython 2.7 Data model
Now you wonder why the heck is it written in lowercase, and not Type? - well because it's implemented in C - it's the same reason defaultdict is lowercase while OrderedDict (in python 2) is normal CamelCase
It's a community wiki answer (so, those who commented with corrections/improvements might consider editing their comments into the answer, if they're sure they are correct).
|
3441

A metaclass is the class of a class. A class defines how an instance of the class (i.e. an object) behaves while a metaclass defines how a class behaves. A class is an instance of a metaclass.

While in Python you can use arbitrary callables for metaclasses (likeJerub shows), the better approach is to make it an actual class itself.type is the usual metaclass in Python.type is itself a class, and it is its own type. You won't be able to recreate something liketype purely in Python, but Python cheats a little. To create your own metaclass in Python you really just want to subclasstype.

A metaclass is most commonly used as a class-factory. When you create an object by calling the class, Python creates a new class (when it executes the 'class' statement) by calling the metaclass. Combined with the normal__init__ and__new__ methods, metaclasses therefore allow you to do 'extra things' when creating a class, like registering the new class with some registry or replace the class with something else entirely.

When theclass statement is executed, Python first executes the body of theclass statement as a normal block of code. The resulting namespace (a dict) holds the attributes of the class-to-be. The metaclass is determined by looking at the baseclasses of the class-to-be (metaclasses are inherited), at the__metaclass__ attribute of the class-to-be (if any) or the__metaclass__ global variable. The metaclass is then called with the name, bases and attributes of the class to instantiate it.

However, metaclasses actually define thetype of a class, not just a factory for it, so you can do much more with them. You can, for instance, define normal methods on the metaclass. These metaclass-methods are like classmethods in that they can be called on the class without an instance, but they are also not like classmethods in that they cannot be called on an instance of the class.type.__subclasses__() is an example of a method on thetype metaclass. You can also define the normal 'magic' methods, like__add__,__iter__ and__getattr__, to implement or change how the class behaves.

Here's an aggregated example of the bits and pieces:

def make_hook(f):    """Decorator to turn 'foo' method into '__foo__'"""    f.is_hook = 1    return fclass MyType(type):    def __new__(mcls, name, bases, attrs):        if name.startswith('None'):            return None        # Go over attributes and see if they should be renamed.        newattrs = {}        for attrname, attrvalue in attrs.iteritems():            if getattr(attrvalue, 'is_hook', 0):                newattrs['__%s__' % attrname] = attrvalue            else:                newattrs[attrname] = attrvalue        return super(MyType, mcls).__new__(mcls, name, bases, newattrs)    def __init__(self, name, bases, attrs):        super(MyType, self).__init__(name, bases, attrs)        # classregistry.register(self, self.interfaces)        print "Would register class %s now." % self    def __add__(self, other):        class AutoClass(self, other):            pass        return AutoClass        # Alternatively, to autogenerate the classname as well as the class:        # return type(self.__name__ + other.__name__, (self, other), {})    def unregister(self):        # classregistry.unregister(self)        print "Would unregister class %s now." % selfclass MyObject:    __metaclass__ = MyTypeclass NoneSample(MyObject):    pass# Will print "NoneType None"print type(NoneSample), repr(NoneSample)class Example(MyObject):    def __init__(self, value):        self.value = value    @make_hook    def add(self, other):        return self.__class__(self.value + other.value)# Will unregister the classExample.unregister()inst = Example(10)# Will fail with an AttributeError#inst.unregister()print inst + instclass Sibling(MyObject):    passExampleSibling = Example + Sibling# ExampleSibling is now a subclass of both Example and Sibling (with no# content of its own) although it will believe it's called 'AutoClass'print ExampleSiblingprint ExampleSibling.__mro__
answeredSep 19, 2008 at 7:01
Thomas Wouters's user avatar

10 Comments

class A(type):pass<NEWLINE>class B(type,metaclass=A):pass<NEWLINE>b.__class__ = b
ppperry he obviously meant you can't recreate type without using type itself as a metaclass. Which is fair enough to say.
Shouldn't unregister() be called by instance of Example class ?
Note that__metaclass__ is not supported in Python 3. In Python 3 useclass MyObject(metaclass=MyType), seepython.org/dev/peps/pep-3115 and the answer below.
The documentation describeshow the metaclass is chosen. The metaclass isn't inherited so much as it is derived. If you specify a metaclass, it has to be a subtype of each base class metaclass; otherwise, you'll use the a base class metaclass that is a subtype of each other base class metaclass. Note that it is possible thatno valid metaclass can be found, and the definition will fail.
|
493

Note, this answer is for Python 2.x as it was written in 2008, metaclasses are slightly different in 3.x.

Metaclasses are the secret sauce that make 'class' work. The default metaclass for a new style object is called 'type'.

class type(object)  |  type(object) -> the object's type  |  type(name, bases, dict) -> a new type

Metaclasses take 3 args. 'name', 'bases' and 'dict'

Here is where the secret starts. Look for where name, bases and the dict come from in this example class definition.

class ThisIsTheName(Bases, Are, Here):    All_the_code_here    def doesIs(create, a):        dict

Lets define a metaclass that will demonstrate how 'class:' calls it.

def test_metaclass(name, bases, dict):    print 'The Class Name is', name    print 'The Class Bases are', bases    print 'The dict has', len(dict), 'elems, the keys are', dict.keys()    return "yellow"class TestName(object, None, int, 1):    __metaclass__ = test_metaclass    foo = 1    def baz(self, arr):        passprint 'TestName = ', repr(TestName)# output => The Class Name is TestNameThe Class Bases are (<type 'object'>, None, <type 'int'>, 1)The dict has 4 elems, the keys are ['baz', '__module__', 'foo', '__metaclass__']TestName =  'yellow'

And now, an example that actually means something, this will automatically make the variables in the list "attributes" set on the class, and set to None.

def init_attributes(name, bases, dict):    if 'attributes' in dict:        for attr in dict['attributes']:            dict[attr] = None    return type(name, bases, dict)class Initialised(object):    __metaclass__ = init_attributes    attributes = ['foo', 'bar', 'baz']print 'foo =>', Initialised.foo# output=>foo => None

Note that the magic behaviour thatInitialised gains by having the metaclassinit_attributes is not passed onto a subclass ofInitialised.

Here is an even more concrete example, showing how you can subclass 'type' to make a metaclass that performs an action when the class is created. This is quite tricky:

class MetaSingleton(type):    instance = None    def __call__(cls, *args, **kw):        if cls.instance is None:            cls.instance = super(MetaSingleton, cls).__call__(*args, **kw)        return cls.instanceclass Foo(object):    __metaclass__ = MetaSingletona = Foo()b = Foo()assert a is b
ralh's user avatar
ralh
2,5941 gold badge16 silver badges19 bronze badges
answeredSep 19, 2008 at 6:26
Jerub's user avatar

Comments

237

Others have explained how metaclasses work and how they fit into the Python type system. Here's an example of what they can be used for. In a testing framework I wrote, I wanted to keep track of the order in which classes were defined, so that I could later instantiate them in this order. I found it easiest to do this using a metaclass.

class MyMeta(type):    counter = 0    def __init__(cls, name, bases, dic):        type.__init__(cls, name, bases, dic)        cls._order = MyMeta.counter        MyMeta.counter += 1class MyType(object):              # Python 2    __metaclass__ = MyMetaclass MyType(metaclass=MyMeta):    # Python 3    pass

Anything that's a subclass ofMyType then gets a class attribute_order that records the order in which the classes were defined.

answeredJun 21, 2011 at 16:30
kindall's user avatar

5 Comments

Thanks for the example. Why did you find this easier than inheriting from MyBase, whose__init__(self) saystype(self)._order = MyBase.counter; MyBase.counter += 1 ?
I wanted the classes themselves, not their instances, to be numbered.
Right, duh. Thanks. My code would reset MyType's attribute on every instantiation, and would never set the attribute if an instance of MyType was never created. Oops. (And a class property could also work, but unlike the metaclass it offers no obvious place to store the counter.)
This is a jolly interesting example, not least because one can genuinely see why a metaclass could be neeeded with this, to supply a solution to a specific difficulty. OTOH I struggle to be convinced that anyone would really need to instantiate objects in the order in which their classes were defined: I guess we just have to take your word for that :).
It was a documentation testing framework and the classes were declarative descriptions of the specific files to be tested, tests to be run, and so forth. The framework reported the results of these in a nicely formatted report grouped by product, document, and test. The report was more useful if it the tests were run in a predictable order. :-)
201

One use for metaclasses is adding new properties and methods to an instance automatically.

For example, if you look atDjango models, their definition looks a bit confusing. It looks as if you are only defining class properties:

class Person(models.Model):    first_name = models.CharField(max_length=30)    last_name = models.CharField(max_length=30)

However, at runtime the Person objects are filled with all sorts of useful methods. See thesource for some amazing metaclassery.

answeredSep 19, 2008 at 6:45
Antti Rasinen's user avatar

1 Comment

Isn't the use of meta classes adding new properties and methods to aclass and not an instance? As far as i understood it the meta class alters the class itself and as a result the instances can be constructed differently by the altered class. Could be a bit misleading to people who try to get the nature of a meta class. Having useful methods on instances can be achieved by normal inherence. The reference to Django code as an example is good, though.
165

I think the ONLamp introduction to metaclass programming is well written and gives a really good introduction to the topic despite being several years old already.

http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html (archived athttps://web.archive.org/web/20080206005253/http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html)

In short: A class is a blueprint for the creation of an instance, a metaclass is a blueprint for the creation of a class. It can be easily seen that in Python classes need to be first-class objects too to enable this behavior.

I've never written one myself, but I think one of the nicest uses of metaclasses can be seen in theDjango framework. The model classes use a metaclass approach to enable a declarative style of writing new models or form classes. While the metaclass is creating the class, all members get the possibility to customize the class itself.

The thing that's left to say is: If you don't know what metaclasses are, the probability that youwill not need them is 99%.

Yet Another User's user avatar
Yet Another User
2,9453 gold badges21 silver badges28 bronze badges
answeredSep 19, 2008 at 6:32
Matthias Kestenholz's user avatar

Comments

151

What are metaclasses? What do you use them for?

TLDR: A metaclass instantiates and defines behavior for a class just like a class instantiates and defines behavior for an instance.

Pseudocode:

>>> Class(...)instance

The above should look familiar. Well, where doesClass come from? It's an instance of a metaclass (also pseudocode):

>>> Metaclass(...)Class

In real code, we can pass the default metaclass,type, everything we need to instantiate a class and we get a class:

>>> type('Foo', (object,), {}) # requires a name, bases, and a namespace<class '__main__.Foo'>

Putting it differently

  • A class is to an instance as a metaclass is to a class.

    When we instantiate an object, we get an instance:

    >>> object()                          # instantiation of class<object object at 0x7f9069b4e0b0>     # instance

    Likewise, when we define a class explicitly with the default metaclass,type, we instantiate it:

    >>> type('Object', (object,), {})     # instantiation of metaclass<class '__main__.Object'>             # instance
  • Put another way, a class is an instance of a metaclass:

    >>> isinstance(object, type)True
  • Put a third way, a metaclass is a class's class.

    >>> type(object) == typeTrue>>> object.__class__<class 'type'>

When you write a class definition and Python executes it, it uses a metaclass to instantiate the class object (which will, in turn, be used to instantiate instances of that class).

Just as we can use class definitions to change how custom object instances behave, we can use a metaclass class definition to change the way a class object behaves.

What can they be used for? From thedocs:

The potential uses for metaclasses are boundless. Some ideas that have been explored include logging, interface checking, automatic delegation, automatic property creation, proxies, frameworks, and automatic resource locking/synchronization.

Nevertheless, it is usually encouraged for users to avoid using metaclasses unless absolutely necessary.

You use a metaclass every time you create a class:

When you write a class definition, for example, like this,

class Foo(object):     'demo'

You instantiate a class object.

>>> Foo<class '__main__.Foo'>>>> isinstance(Foo, type), isinstance(Foo, object)(True, True)

It is the same as functionally callingtype with the appropriate arguments and assigning the result to a variable of that name:

name = 'Foo'bases = (object,)namespace = {'__doc__': 'demo'}Foo = type(name, bases, namespace)

Note, some things automatically get added to the__dict__, i.e., the namespace:

>>> Foo.__dict__dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': 'demo'})

Themetaclass of the object we created, in both cases, istype.

(A side-note on the contents of the class__dict__:__module__ is there because classes must know where they are defined, and__dict__ and__weakref__ are there because we don't define__slots__ - if wedefine__slots__ we'll save a bit of space in the instances, as we can disallow__dict__ and__weakref__ by excluding them. For example:

>>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})>>> Baz.__dict__mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})

... but I digress.)

We can extendtype just like any other class definition:

Here's the default__repr__ of classes:

>>> Foo<class '__main__.Foo'>

One of the most valuable things we can do by default in writing a Python object is to provide it with a good__repr__. When we callhelp(repr) we learn that there's a good test for a__repr__ that also requires a test for equality -obj == eval(repr(obj)). The following simple implementation of__repr__ and__eq__ for class instances of our type class provides us with a demonstration that may improve on the default__repr__ of classes:

class Type(type):    def __repr__(cls):        """        >>> Baz        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})        >>> eval(repr(Baz))        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})        """        metaname = type(cls).__name__        name = cls.__name__        parents = ', '.join(b.__name__ for b in cls.__bases__)        if parents:            parents += ','        namespace = ', '.join(': '.join(          (repr(k), repr(v) if not isinstance(v, type) else v.__name__))               for k, v in cls.__dict__.items())        return '{0}(\'{1}\', ({2}), {{{3}}})'.format(metaname, name, parents, namespace)    def __eq__(cls, other):        """        >>> Baz == eval(repr(Baz))        True                    """        return (cls.__name__, cls.__bases__, cls.__dict__) == (                other.__name__, other.__bases__, other.__dict__)

So now when we create an object with this metaclass, the__repr__ echoed on the command line provides a much less ugly sight than the default:

>>> class Bar(object): pass>>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})>>> BazType('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})

With a nice__repr__ defined for the class instance, we have a stronger ability to debug our code. However, much further checking witheval(repr(Class)) is unlikely (as functions would be rather impossible to eval from their default__repr__'s).

An expected usage:__prepare__ a namespace

If, for example, we want to know in what order a class's methods are created in, we could provide an ordered dict as the namespace of the class. We would do this with__prepare__ whichreturns the namespace dict for the class if it is implemented in Python 3:

from collections import OrderedDictclass OrderedType(Type):    @classmethod    def __prepare__(metacls, name, bases, **kwargs):        return OrderedDict()    def __new__(cls, name, bases, namespace, **kwargs):        result = Type.__new__(cls, name, bases, dict(namespace))        result.members = tuple(namespace)        return result

And usage:

class OrderedMethodsObject(object, metaclass=OrderedType):    def method1(self): pass    def method2(self): pass    def method3(self): pass    def method4(self): pass

And now we have a record of the order in which these methods (and other class attributes) were created:

>>> OrderedMethodsObject.members('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4')

Note, this example was adapted from thedocumentation - the newenum in the standard library does this.

So what we did was instantiate a metaclass by creating a class. We can also treat the metaclass as we would any other class. It has a method resolution order:

>>> inspect.getmro(OrderedType)(<class '__main__.OrderedType'>, <class '__main__.Type'>, <class 'type'>, <class 'object'>)

And it has approximately the correctrepr (which we can no longer eval unless we can find a way to represent our functions.):

>>> OrderedMethodsObjectOrderedType('OrderedMethodsObject', (object,), {'method1': <function OrderedMethodsObject.method1 at 0x0000000002DB01E0>, 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': <function OrderedMethodsObject.method3 at 0x0000000002DB02F0>, 'method2': <function OrderedMethodsObject.method2 at 0x0000000002DB0268>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'OrderedMethodsObject' objects>, '__doc__': None, '__dict__': <attribute '__dict__' of 'OrderedMethodsObject' objects>, 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>})
answeredAug 10, 2015 at 23:28
Aaron Hall's user avatar

Comments

113

Python 3 update

There are (at this point) two key methods in a metaclass:

  • __prepare__, and
  • __new__

__prepare__ lets you supply a custom mapping (such as anOrderedDict) to be used as the namespace while the class is being created. You must return an instance of whatever namespace you choose. If you don't implement__prepare__ a normaldict is used.

__new__ is responsible for the actual creation/modification of the final class.

A bare-bones, do-nothing-extra metaclass would like:

class Meta(type):    def __prepare__(metaclass, cls, bases):        return dict()    def __new__(metacls, cls, bases, clsdict):        return super().__new__(metacls, cls, bases, clsdict)

A simple example:

Say you want some simple validation code to run on your attributes -- like it must always be anint or astr. Without a metaclass, your class would look something like:

class Person:    weight = ValidateType('weight', int)    age = ValidateType('age', int)    name = ValidateType('name', str)

As you can see, you have to repeat the name of the attribute twice. This makes typos possible along with irritating bugs.

A simple metaclass can address that problem:

class Person(metaclass=Validator):    weight = ValidateType(int)    age = ValidateType(int)    name = ValidateType(str)

This is what the metaclass would look like (not using__prepare__ since it is not needed):

class Validator(type):    def __new__(metacls, cls, bases, clsdict):        # search clsdict looking for ValidateType descriptors        for name, attr in clsdict.items():            if isinstance(attr, ValidateType):                attr.name = name                attr.attr = '_' + name        # create final class and return it        return super().__new__(metacls, cls, bases, clsdict)

A sample run of:

p = Person()p.weight = 9print(p.weight)p.weight = '9'

produces:

9Traceback (most recent call last):  File "simple_meta.py", line 36, in <module>    p.weight = '9'  File "simple_meta.py", line 24, in __set__    (self.name, self.type, value))TypeError: weight must be of type(s) <class 'int'> (got '9')

Note: This example is simple enough it could have also been accomplished with a class decorator, but presumably an actual metaclass would be doing much more.

The 'ValidateType' class for reference:

class ValidateType:    def __init__(self, type):        self.name = None  # will be set by metaclass        self.attr = None  # will be set by metaclass        self.type = type    def __get__(self, inst, cls):        if inst is None:            return self        else:            return inst.__dict__[self.attr]    def __set__(self, inst, value):        if not isinstance(value, self.type):            raise TypeError('%s must be of type(s) %s (got %r)' %                    (self.name, self.type, value))        else:            inst.__dict__[self.attr] = value
answeredMar 1, 2016 at 19:48
Ethan Furman's user avatar

1 Comment

Note that since python 3.6, you can use__set_name__(cls, name) in the descriptor (ValidateType) to set the name in the descriptor (self.name and in this case alsoself.attr). This was added to not have to dive into metaclasses for this specific common use case (see PEP 487).
105

Role of a metaclass'__call__() method when creating a class instance

If you've done Python programming for more than a few months you'll eventually stumble upon code that looks like this:

# define a classclass SomeClass(object):    # ...    # some definition here ...    # ...# create an instance of itinstance = SomeClass()# then call the object as if it's a functionresult = instance('foo', 'bar')

The latter is possible when you implement the__call__() magic method on the class.

class SomeClass(object):    # ...    # some definition here ...    # ...    def __call__(self, foo, bar):        return bar + foo

The__call__() method is invoked when an instance of a class is used as a callable. But as we've seen from previous answers a class itself is an instance of a metaclass, so when we use the class as a callable (i.e. when we create an instance of it) we're actually calling its metaclass'__call__() method. At this point most Python programmers are a bit confused because they've been told that when creating an instance like thisinstance = SomeClass() you're calling its__init__() method. Some who've dug a bit deeper know that before__init__() there's__new__(). Well, today another layer of truth is being revealed, before__new__() there's the metaclass'__call__().

Let's study the method call chain from specifically the perspective of creating an instance of a class.

This is a metaclass that logs exactly the moment before an instance is created and the moment it's about to return it.

class Meta_1(type):    def __call__(cls):        print "Meta_1.__call__() before creating an instance of ", cls        instance = super(Meta_1, cls).__call__()        print "Meta_1.__call__() about to return instance."        return instance

This is a class that uses that metaclass

class Class_1(object):    __metaclass__ = Meta_1    def __new__(cls):        print "Class_1.__new__() before creating an instance."        instance = super(Class_1, cls).__new__(cls)        print "Class_1.__new__() about to return instance."        return instance    def __init__(self):        print "entering Class_1.__init__() for instance initialization."        super(Class_1,self).__init__()        print "exiting Class_1.__init__()."

And now let's create an instance ofClass_1

instance = Class_1()# Meta_1.__call__() before creating an instance of <class '__main__.Class_1'>.# Class_1.__new__() before creating an instance.# Class_1.__new__() about to return instance.# entering Class_1.__init__() for instance initialization.# exiting Class_1.__init__().# Meta_1.__call__() about to return instance.

Observe that the code above doesn't actually do anything more than logging the tasks. Each method delegates the actual work to its parent's implementation, thus keeping the default behavior. Sincetype isMeta_1's parent class (type being the default parent metaclass) and considering the ordering sequence of the output above, we now have a clue as to what would be the pseudo implementation oftype.__call__():

class type:    def __call__(cls, *args, **kwarg):        # ... maybe a few things done to cls here        # then we call __new__() on the class to create an instance        instance = cls.__new__(cls, *args, **kwargs)        # ... maybe a few things done to the instance here        # then we initialize the instance with its __init__() method        instance.__init__(*args, **kwargs)        # ... maybe a few more things done to instance here        # then we return it        return instance

We can see that the metaclass'__call__() method is the one that's called first. It then delegates creation of the instance to the class's__new__() method and initialization to the instance's__init__(). It's also the one that ultimately returns the instance.

From the above it stems that the metaclass'__call__() is also given the opportunity to decide whether or not a call toClass_1.__new__() orClass_1.__init__() will eventually be made. Over the course of its execution it could actually return an object that hasn't been touched by either of these methods. Take for example this approach to the singleton pattern:

class Meta_2(type):    singletons = {}    def __call__(cls, *args, **kwargs):        if cls in Meta_2.singletons:            # we return the only instance and skip a call to __new__()            # and __init__()            print ("{} singleton returning from Meta_2.__call__(), "                   "skipping creation of new instance.".format(cls))            return Meta_2.singletons[cls]        # else if the singleton isn't present we proceed as usual        print "Meta_2.__call__() before creating an instance."        instance = super(Meta_2, cls).__call__(*args, **kwargs)        Meta_2.singletons[cls] = instance        print "Meta_2.__call__() returning new instance."        return instanceclass Class_2(object):    __metaclass__ = Meta_2    def __new__(cls, *args, **kwargs):        print "Class_2.__new__() before creating instance."        instance = super(Class_2, cls).__new__(cls)        print "Class_2.__new__() returning instance."        return instance    def __init__(self, *args, **kwargs):        print "entering Class_2.__init__() for initialization."        super(Class_2, self).__init__()        print "exiting Class_2.__init__()."

Let's observe what happens when repeatedly trying to create an object of typeClass_2

a = Class_2()# Meta_2.__call__() before creating an instance.# Class_2.__new__() before creating instance.# Class_2.__new__() returning instance.# entering Class_2.__init__() for initialization.# exiting Class_2.__init__().# Meta_2.__call__() returning new instance.b = Class_2()# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.c = Class_2()# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.a is b is c # True
answeredOct 13, 2016 at 9:21
Michael Ekoka's user avatar

1 Comment

This is a good addition to the previously upvoted "accepted answer". It provides examples for intermediate coders to chew on.
78

Ametaclass is a class that tells how (some) other class should be created.

This is a case where I sawmetaclass as a solution to my problem:I had a really complicated problem, that probably could have been solved differently, but I chose to solve it using ametaclass. Because of the complexity, it is one of the few modules I have written where the comments in the module surpass the amount of code that has been written. Here it is...

#!/usr/bin/env python# Copyright (C) 2013-2014 Craig Phillips.  All rights reserved.# This requires some explaining.  The point of this metaclass excercise is to# create a static abstract class that is in one way or another, dormant until# queried.  I experimented with creating a singlton on import, but that did# not quite behave how I wanted it to.  See now here, we are creating a class# called GsyncOptions, that on import, will do nothing except state that its# class creator is GsyncOptionsType.  This means, docopt doesn't parse any# of the help document, nor does it start processing command line options.# So importing this module becomes really efficient.  The complicated bit# comes from requiring the GsyncOptions class to be static.  By that, I mean# any property on it, may or may not exist, since they are not statically# defined; so I can't simply just define the class with a whole bunch of# properties that are @property @staticmethods.## So here's how it works:## Executing 'from libgsync.options import GsyncOptions' does nothing more# than load up this module, define the Type and the Class and import them# into the callers namespace.  Simple.## Invoking 'GsyncOptions.debug' for the first time, or any other property# causes the __metaclass__ __getattr__ method to be called, since the class# is not instantiated as a class instance yet.  The __getattr__ method on# the type then initialises the class (GsyncOptions) via the __initialiseClass# method.  This is the first and only time the class will actually have its# dictionary statically populated.  The docopt module is invoked to parse the# usage document and generate command line options from it.  These are then# paired with their defaults and what's in sys.argv.  After all that, we# setup some dynamic properties that could not be defined by their name in# the usage, before everything is then transplanted onto the actual class# object (or static class GsyncOptions).## Another piece of magic, is to allow command line options to be set in# in their native form and be translated into argparse style properties.## Finally, the GsyncListOptions class is actually where the options are# stored.  This only acts as a mechanism for storing options as lists, to# allow aggregation of duplicate options or options that can be specified# multiple times.  The __getattr__ call hides this by default, returning the# last item in a property's list.  However, if the entire list is required,# calling the 'list()' method on the GsyncOptions class, returns a reference# to the GsyncListOptions class, which contains all of the same properties# but as lists and without the duplication of having them as both lists and# static singlton values.## So this actually means that GsyncOptions is actually a static proxy class...## ...And all this is neatly hidden within a closure for safe keeping.def GetGsyncOptionsType():    class GsyncListOptions(object):        __initialised = False    class GsyncOptionsType(type):        def __initialiseClass(cls):            if GsyncListOptions._GsyncListOptions__initialised: return            from docopt import docopt            from libgsync.options import doc            from libgsync import __version__            options = docopt(                doc.__doc__ % __version__,                version = __version__,                options_first = True            )            paths = options.pop('<path>', None)            setattr(cls, "destination_path", paths.pop() if paths else None)            setattr(cls, "source_paths", paths)            setattr(cls, "options", options)            for k, v in options.iteritems():                setattr(cls, k, v)            GsyncListOptions._GsyncListOptions__initialised = True        def list(cls):            return GsyncListOptions        def __getattr__(cls, name):            cls.__initialiseClass()            return getattr(GsyncListOptions, name)[-1]        def __setattr__(cls, name, value):            # Substitut option names: --an-option-name for an_option_name            import re            name = re.sub(r'^__', "", re.sub(r'-', "_", name))            listvalue = []            # Ensure value is converted to a list type for GsyncListOptions            if isinstance(value, list):                if value:                    listvalue = [] + value                else:                    listvalue = [ None ]            else:                listvalue = [ value ]            type.__setattr__(GsyncListOptions, name, listvalue)    # Cleanup this module to prevent tinkering.    import sys    module = sys.modules[__name__]    del module.__dict__['GetGsyncOptionsType']    return GsyncOptionsType# Our singlton abstract proxy class.class GsyncOptions(object):    __metaclass__ = GetGsyncOptionsType()

1 Comment

pylint says your code has been rated at -1.03/10.
65

The tl;dr version

Thetype(obj) function gets you the type of an object.

Thetype() of a class is itsmetaclass.

To use a metaclass:

class Foo(object):    __metaclass__ = MyMetaClass

type is its own metaclass. The class of a class is a metaclass-- the body of a class is the arguments passed to the metaclass that is used to construct the class.

Here you can read about how to use metaclasses to customize class construction.

answeredDec 27, 2016 at 2:21
noɥʇʎԀʎzɐɹƆ's user avatar

Comments

60

type is actually ametaclass -- a class that creates another classes.Mostmetaclass are the subclasses oftype. Themetaclass receives thenew class as its first argument and provide access to class object with details as mentioned below:

>>> class MetaClass(type):...     def __init__(cls, name, bases, attrs):...         print ('class name: %s' %name )...         print ('Defining class %s' %cls)...         print('Bases %s: ' %bases)...         print('Attributes')...         for (name, value) in attrs.items():...             print ('%s :%r' %(name, value))... >>> class NewClass(object, metaclass=MetaClass):...    get_choch='dairy'... class name: NewClassBases <class 'object'>: Defining class <class 'NewClass'>get_choch :'dairy'__module__ :'builtins'__qualname__ :'NewClass'

Note:

Notice that the class was not instantiated at any time; the simple act of creating the class triggered execution of themetaclass.

Chankey Pathak's user avatar
Chankey Pathak
21.8k12 gold badges88 silver badges137 bronze badges
answeredAug 9, 2016 at 18:49
Mushahid Khan's user avatar

Comments

43

Python classes are themselves objects - as in instance - of their meta-class.

The default metaclass, which is applied when when you determine classes as:

class foo:    ...

meta class are used to apply some rule to an entire set of classes. For example, suppose you're building an ORM to access a database, and you want records from each table to be of a class mapped to that table (based on fields, business rules, etc..,), a possible use of metaclass is for instance, connection pool logic, which is share by all classes of record from all tables. Another use is logic to to support foreign keys, which involves multiple classes of records.

when you define metaclass, you subclass type, and can overrided the following magic methods to insert your logic.

class somemeta(type):    __new__(mcs, name, bases, clsdict):      """  mcs: is the base metaclass, in this case type.  name: name of the new class, as provided by the user.  bases: tuple of base classes   clsdict: a dictionary containing all methods and attributes defined on class  you must return a class object by invoking the __new__ constructor on the base metaclass.  ie:     return type.__call__(mcs, name, bases, clsdict).  in the following case:  class foo(baseclass):        __metaclass__ = somemeta  an_attr = 12  def bar(self):      ...  @classmethod  def foo(cls):      ...      arguments would be : ( somemeta, "foo", (baseclass, baseofbase,..., object), {"an_attr":12, "bar": <function>, "foo": <bound class method>}      you can modify any of these values before passing on to type      """      return type.__call__(mcs, name, bases, clsdict)    def __init__(self, name, bases, clsdict):      """       called after type has been created. unlike in standard classes, __init__ method cannot modify the instance (cls) - and should be used for class validaton.      """      pass    def __prepare__():        """        returns a dict or something that can be used as a namespace.        the type will then attach methods and attributes from class definition to it.        call order :        somemeta.__new__ ->  type.__new__ -> type.__init__ -> somemeta.__init__         """        return dict()    def mymethod(cls):        """ works like a classmethod, but for class objects. Also, my method will not be visible to instances of cls.        """        pass

anyhow, those two are the most commonly used hooks. metaclassing is powerful, and above is nowhere near and exhaustive list of uses for metaclassing.

answeredJul 13, 2017 at 7:58
Xingzhou Liu's user avatar

Comments

41

The type() function can return the type of an object or create a new type,

for example, we can create a Hi class with the type() function and do not need to use this way with class Hi(object):

def func(self, name='mike'):    print('Hi, %s.' % name)Hi = type('Hi', (object,), dict(hi=func))h = Hi()h.hi()Hi, mike.type(Hi)typetype(h)__main__.Hi

In addition to using type() to create classes dynamically, you can control creation behavior of class and use metaclass.

According to the Python object model, the class is the object, so the class must be an instance of another certain class.By default, a Python class is instance of the type class. That is, type is metaclass of most of the built-in classes and metaclass of user-defined classes.

class ListMetaclass(type):    def __new__(cls, name, bases, attrs):        attrs['add'] = lambda self, value: self.append(value)        return type.__new__(cls, name, bases, attrs)class CustomList(list, metaclass=ListMetaclass):    passlst = CustomList()lst.add('custom_list_1')lst.add('custom_list_2')lst['custom_list_1', 'custom_list_2']

Magic will take effect when we passed keyword arguments in metaclass, it indicates the Python interpreter to create the CustomList through ListMetaclass.new (), at this point, we can modify the class definition, for example, and add a new method and then return the revised definition.

answeredJan 12, 2018 at 9:16
binbjz's user avatar

Comments

30

In addition to the published answers I can say that ametaclass defines the behaviour for a class. So, you can explicitly set your metaclass. Whenever Python gets a keywordclass then it starts searching for themetaclass. If it's not found – the default metaclass type is used to create the class's object. Using the__metaclass__ attribute, you can setmetaclass of your class:

class MyClass:   __metaclass__ = type   # write here other method   # write here one more methodprint(MyClass.__metaclass__)

It'll produce the output like this:

class 'type'

And, of course, you can create your ownmetaclass to define the behaviour of any class that are created using your class.

For doing that, your defaultmetaclass type class must be inherited as this is the mainmetaclass:

class MyMetaClass(type):   __metaclass__ = type   # you can write here any behaviour you wantclass MyTestClass:   __metaclass__ = MyMetaClassObj = MyTestClass()print(Obj.__metaclass__)print(MyMetaClass.__metaclass__)

The output will be:

class '__main__.MyMetaClass'class 'type'
answeredSep 15, 2018 at 12:41
Andy Jazz's user avatar

Comments

22

Note that in python 3.6 a new dunder method__init_subclass__(cls, **kwargs) was introduced to replace a lot of common use cases for metaclasses. Is is called when a subclass of the defining class is created. Seepython docs.

answeredMar 3, 2020 at 10:06
Lars's user avatar

Comments

20

Here's another example of what it can be used for:

  • You can use themetaclass to change the function of its instance (the class).
class MetaMemberControl(type):    __slots__ = ()    @classmethod    def __prepare__(mcs, f_cls_name, f_cls_parents,  # f_cls means: future class                    meta_args=None, meta_options=None):  # meta_args and meta_options is not necessarily needed, just so you know.        f_cls_attr = dict()        if not "do something or if you want to define your cool stuff of dict...":            return dict(make_your_special_dict=None)        else:            return f_cls_attr    def __new__(mcs, f_cls_name, f_cls_parents, f_cls_attr,                meta_args=None, meta_options=None):        original_getattr = f_cls_attr.get('__getattribute__')        original_setattr = f_cls_attr.get('__setattr__')        def init_getattr(self, item):            if not item.startswith('_'):  # you can set break points at here                alias_name = '_' + item                if alias_name in f_cls_attr['__slots__']:                    item = alias_name            if original_getattr is not None:                return original_getattr(self, item)            else:                return super(eval(f_cls_name), self).__getattribute__(item)        def init_setattr(self, key, value):            if not key.startswith('_') and ('_' + key) in f_cls_attr['__slots__']:                raise AttributeError(f"you can't modify private members:_{key}")            if original_setattr is not None:                original_setattr(self, key, value)            else:                super(eval(f_cls_name), self).__setattr__(key, value)        f_cls_attr['__getattribute__'] = init_getattr        f_cls_attr['__setattr__'] = init_setattr        cls = super().__new__(mcs, f_cls_name, f_cls_parents, f_cls_attr)        return clsclass Human(metaclass=MetaMemberControl):    __slots__ = ('_age', '_name')    def __init__(self, name, age):        self._name = name        self._age = age    def __getattribute__(self, item):        """        is just for IDE recognize.        """        return super().__getattribute__(item)    """ with MetaMemberControl then you don't have to write as following    @property    def name(self):        return self._name    @property    def age(self):        return self._age    """def test_demo():    human = Human('Carson', 27)    # human.age = 18  # you can't modify private members:_age  <-- this is defined by yourself.    # human.k = 18  # 'Human' object has no attribute 'k'  <-- system error.    age1 = human._age  # It's OK, although the IDE will show some warnings. (Access to a protected member _age of a class)    age2 = human.age  # It's OK! see below:    """    if you do not define `__getattribute__` at the class of Human,    the IDE will show you: Unresolved attribute reference 'age' for class 'Human'    but it's ok on running since the MetaMemberControl will help you.    """if __name__ == '__main__':    test_demo()

Themetaclass is powerful, there are many things (such as monkey magic) you can do with it, but be careful this may only be known to you.

answeredDec 20, 2019 at 11:03
Carson's user avatar

Comments

18

The top answer is correct.

But readers may be coming here searching answers about similarly named inner classes. They are present in popular libraries, such asDjango andWTForms.

As DavidW points out in the comments beneath this answer,these arelibrary-specific features and are not to be confused with the advanced, unrelatedPython language feature with a similar name.

Rather, these are namespaces within classes' dicts. They are constructed using inner classes for sake of readability.

In this example special field,abstract is visibly separate from fields of Author model.

from django.db import modelsclass Author(models.Model):    name = models.CharField(max_length=50)    email = models.EmailField()    class Meta:        abstract = True

Another example is from the documentation forWTForms:

from wtforms.form import Formfrom wtforms.csrf.session import SessionCSRFfrom wtforms.fields import StringFieldclass MyBaseForm(Form):    class Meta:        csrf = True        csrf_class = SessionCSRF    name = StringField("name")

This syntax does not get special treatment in the python programming language.Meta is not a keyword here, and does not trigger metaclass behavior. Rather, third-party library code in packages likeDjango andWTForms reads this property in the constructors of certain classes, and elsewhere.

The presence of these declarations modifies the behavior of the classes that have these declarations. For example,WTForms readsself.Meta.csrf to determine if the form needs acsrf field.

Alex Waygood's user avatar
Alex Waygood
7,7893 gold badges31 silver badges50 bronze badges
answeredJul 17, 2021 at 5:18
Manukumar's user avatar

4 Comments

This is a Django-specific feature where a nested class calledMeta has a special meaning. The question is about an unrelated Python language feature with a similar name.
@DavidW — hamilyon undertook a heroic edit of this post. It's now, in my opinion, quite a useful answer.
@AlexWaygood I'd probably have rejected the edit (too big a change...) but I can see that it does clarify something that's a point of confusion so it probably is useful. With that in mind, I've removed my downvote.
@DavidW yeah, I think you could argue it both ways. I wouldn't normally approve an edit that large. But I felt like it kept to the spirit of the original post, and it seemed like a fair amount of work had gone into a noble endeavour (clarifying a legitimate point of confusion), so decided to approve.
17

In object-oriented programming, a metaclass is a class whose instances are classes. Just as an ordinary class defines the behavior of certain objects, a metaclass defines the behavior of certain class and their instancesThe term metaclass simply means something used to create classes. In other words, it is the class of a class. The metaclass is used to create the class so like the object being an instance of a class, a class is an instance of a metaclass. In python classes are also considered objects.

answeredJul 9, 2019 at 5:37
Venu Gopal Tewari's user avatar

2 Comments

Rather than giving bookish definitions, would have been better if you had added some examples. The first line of your answer seems to have been copied from the Wikipedia entry of Metaclasses.
@verisimilitude I am also learning can you help me improving this answer by providing some practical examples from your experience ??
17

A class, in Python, is an object, and just like any other object, it is an instance of "something". This "something" is what is termed as a Metaclass. This metaclass is a special type of class that creates other class's objects. Hence, metaclass is responsible for making new classes. This allows the programmer to customize the way classes are generated.

To create a metaclass, overriding ofnew() andinit() methods is usually done.new() can be overridden to change the way objects are created, whileinit() can be overridden to change the way of initializing the object. Metaclass can be created by a number of ways. One of the ways is to use type() function. type() function, when called with 3 parameters, creates a metaclass. The parameters are :-

  1. Class Name
  2. Tuple having base classes inherited by class
  3. A dictionary having all class methods and class variables

Another way of creating a metaclass comprises of 'metaclass' keyword. Define the metaclass as a simple class. In the parameters of inherited class, pass metaclass=metaclass_name

Metaclass can be specifically used in the following situations :-

  1. when a particular effect has to be applied to all the subclasses
  2. Automatic change of class (on creation) is required
  3. By API developers
answeredJan 20, 2020 at 6:59
Swati Srivastava's user avatar

Comments

12

I saw an interesting use case for metaclasses in a package calledclassutilities. It checks if all class variables are in upper case format (it is convenient to have unified logic for configuration classes), and checks if there are no instance level methods in class.Another interesting example for metaclases was deactivation of unittests based on complex conditions (checking values of multiple environmental variables).

answeredJul 12, 2021 at 22:51
Emma Brown's user avatar

Comments

11

In Python, a metaclass is a subclass of a subclass that determines how a subclass behaves. A class is an instance of another metaclass. In Python, a class specifies how the class's instance will behave.

Since metaclasses are in charge of class generation, you can write your own custom metaclasses to change how classes are created by performing additional actions or injecting code. Custom metaclasses aren't always important, but they can be.

answeredApr 21, 2021 at 18:41
OfficialNebz's user avatar

Comments

3

look this:

Python 3.10.0rc2 (tags/v3.10.0rc2:839d789, Sep  7 2021, 18:51:45) [MSC v.1929 64 bit (AMD64)] on win32Type "help", "copyright", "credits" or "license" for more information.>>> class Object:...     pass... >>> class Meta(type):...     test = 'Worked!!!'...     def __repr__(self):...             return 'This is "Meta" metaclass'... >>> class ObjectWithMetaClass(metaclass=Meta):...     pass... >>> Object or type(Object())<class '__main__.Object'>>>> ObjectWithMetaClass or type(ObjectWithMetaClass())This is "Meta" metaclass>>> Object.testAttributeError: ...>>> ObjectWithMetaClass.test'Worked!!!'>>> type(Object)<class 'type'>>>> type(ObjectWithMetaClass)<class '__main__.Meta'>>>> type(type(ObjectWithMetaClass))<class 'type'>>>> Object.__bases__(<class 'object'>,)>>> ObjectWithMetaClass.__bases__(<class 'object'>,)>>> type(ObjectWithMetaClass).__bases__(<class 'type'>,)>>> Object.__mro__(<class '__main__.Object'>, <class 'object'>)>>> ObjectWithMetaClass.__mro__(This is "Meta" metaclass, <class 'object'>)>>>

In other words, when an object was not created (type of object), we looking MetaClass.

answeredOct 3, 2021 at 16:06
Delta's user avatar

Comments

3

i want to add a little on whytype.__new__() overtype()

first, take a look at following classes

In [1]: class MyMeta(type):   ...:     def __new__(cls, cls_name, bases, attrs):   ...:         print(cls, cls_name, bases, attrs)   ...:         return super().__new__(cls, cls_name, bases, attrs)   ...:In [2]: class AClass(metaclass=MyMeta):   ...:     pass   ...:<class '__main__.MyMeta'> AClass () {'__module__': '__main__', '__qualname__': 'AClass'}In [3]: class BClass:   ...:     pass   ...:In [4]: AClass.__class__Out[4]: __main__.MyMetaIn [5]: BClass.__class__Out[5]: typeIn [6]: class SubAClass(AClass):   ...:     pass   ...:<class '__main__.MyMeta'> SubAClass (<class '__main__.AClass'>,) {'__module__': '__main__', '__qualname__': 'SubAClass'}
  1. type.__new__ just assignedMyMeta toAClass.__class__.

    how?type.__new__ would take the first parameter cls, which

    is MyMeta, and executeAClass.__class__ = MyMeta.

    when we tried to create SubAClass a subclass of AClass, Python would

    take a look at the metaclass we designated to be used to create SubAClass

    and in this case, we did not pass a metaclass for SubAClass, so Python got a None for metaclass.

    then Python would try to pick up the metaclass of the first base class of SubAClass, apparently it gotMyMeta.

  2. if you calledtype() instead oftype.__new__, then we

    would haveAClass.__class__ to betype. why?

    type() still callstype.__new__ but passestype as the first parameter implicitly.

    that means AClass would be equivalent of BClass, both of them have type

    as their__class__ attr

how the searching of metaclass works in C code?

it works pretty much like what we've just mentioned

the functionbuiltin___build_class__ would be called when you defined a class

and code is just so straightforward

static PyObject *builtin___build_class__(PyObject *self, PyObject *const *args, Py_ssize_t nargs,                        PyObject *kwnames){    if (meta == NULL) {        /* if there are no bases, use type: */        if (PyTuple_GET_SIZE(bases) == 0) {            meta = (PyObject *) (&PyType_Type);        }        /* else get the type of the first base */        else {            PyObject *base0 = PyTuple_GET_ITEM(bases, 0);            meta = (PyObject *)Py_TYPE(base0);        }        Py_INCREF(meta);        isclass = 1;  /* meta is really a class */    }    PyObject *margs[3] = {name, bases, ns};    cls = PyObject_VectorcallDict(meta, margs, 3, mkw);}

basically,meta = (PyObject *)Py_TYPE(base0); is everything we want to know

it can be translated to bemeta = Py_TYPE(AClass) = MyMeta = AClass.__class__

answeredFeb 11, 2023 at 7:55
user3197067's user avatar

Comments

1

If you are familiar with meta-programing. you should known meta-programing is a kind of programing that controls programing.

In Python, metaclass is a way of meta-programing.

You can use metaclass to create class.

Follow rules:

  • Metaclass is used tocreate class.
  • Python build-in the metaclass. calledtype
  • You can customize own metaclass by inheritingtype
  • By setting a class's metaclass, you can change itsdefault behavior.

Code example:

# create class A by 'type'A = type('A', (), {'val': 5})print('A val is', A().val)# create class B by 'type' and add attr is call valB = type('B', (A,), {'val': 10})print('B val is', B().val)# customize own metaclassclass CustomizeMetaclass(type):    def __new__(mcs, name, bases, attrs):        # All class created by CustomizeMetaclass has add attr val        attrs['val'] = 20        return super(CustomizeMetaclass, mcs).__new__(            mcs, name, bases, attrs)    def __init__(cls, name, bases, attrs):        super(CustomizeMetaclass, cls).__init__(            name, bases, attrs)C = CustomizeMetaclass('C', (), {})print('C val is', C().val)class D(object, metaclass=CustomizeMetaclass):    passprint('D val is', D().val)

output:

A val is 5B val is 10C val is 20D val is 20
Mathieu Rodic's user avatar
Mathieu Rodic
6,7702 gold badges45 silver badges49 bronze badges
answeredMay 19, 2024 at 8:45
hupo's user avatar

Comments

-1

In Python or in any other language we have a type for every variable or object we declare. For getting type of anything(variable,object,etc.) in Python we can use type() function.

Bypassing the metaclass keyword in the class definition we can customize the class creation process.

class meta(type):    passclass baseclass(metaclass=meta): # This is Mestaclass    passclass derivedclass(baseclass):    passprint(type(meta))print(type(baseclass))print(type(derivedclass))

When defining a new class if no metaclass is defined the default type metaclass is used. If a given metaclass is not the object(instance) of type(), in that situation it is used directly as a metaclass.

answeredFeb 4, 2023 at 6:35
Neha Sharma's user avatar

Comments

Protected question. To answer this question, you need to have at least 10 reputation on this site (not counting theassociation bonus). The reputation requirement helps protect this question from spam and non-answer activity.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.