
Table of Contents
Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding:Metaclasses in Python
The termmetaprogramming refers to the potential for a program to have knowledge of or manipulate itself. Python supports a form of metaprogramming for classes calledmetaclasses.
Metaclasses are an esotericOOP concept, lurking behind virtually all Python code. You are using them whether you are aware of it or not. For the most part, you don’t need to be aware of it. Most Python programmers rarely, if ever, have to think about metaclasses.
When the need arises, however, Python provides a capability that not all object-oriented languages support: you can get under the hood and define custom metaclasses. The use of custom metaclasses is somewhat controversial, as suggested by the following quote from Tim Peters, the Python guru who authored theZen of Python:
“Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why).”
—Tim Peters
There are Pythonistas (as Python aficionados are known) who believe that you should never use custom metaclasses. That might be going a bit far, but it is probably true that custom metaclasses mostly aren’t necessary. If it isn’t pretty obvious that a problem calls for them, then it will probably be cleaner and more readable if solved in a simpler way.
Still, understanding Python metaclasses is worthwhile, because it leads to a better understanding of the internals ofPython classes in general. You never know: you may one day find yourself in one of those situations where you just know that a custom metaclass is what you want.
Get Notified: Don’t miss the follow up to this tutorial—Click here to join the Real Python Newsletter and you’ll know when the next installment comes out.
In the Python realm, a classcan be one of two varieties. No official terminology has been decided on, so they are informally referred to as old-style and new-style classes.
With old-style classes, class and type are not quite the same thing. An instance of an old-style class is always implemented from a single built-in type calledinstance. Ifobj is an instance of an old-style class,obj.__class__ designates the class, buttype(obj) is alwaysinstance. The following example is taken from Python 2.7:
>>>classFoo:...pass...>>>x=Foo()>>>x.__class__<class __main__.Foo at 0x000000000535CC48>>>>type(x)<type 'instance'>New-style classes unify the concepts of class and type. Ifobj is an instance of a new-style class,type(obj) is the same asobj.__class__:
>>>classFoo:...pass>>>obj=Foo()>>>obj.__class__<class '__main__.Foo'>>>>type(obj)<class '__main__.Foo'>>>>obj.__class__istype(obj)True>>>n=5>>>d={'x':1,'y':2}>>>classFoo:...pass...>>>x=Foo()>>>forobjin(n,d,x):...print(type(obj)isobj.__class__)...TrueTrueTrueIn Python 3, all classes are new-style classes. Thus, in Python 3 it is reasonable to refer to an object’s type and its class interchangeably.
Note: In Python 2, classes are old-style by default. Prior to Python 2.2, new-style classes weren’t supported at all. From Python 2.2 onward, they can be created but must be explicitly declared as new-style.
Remember that,in Python, everything is an object. Classes are objects as well. As a result, a class must have a type. What is the type of a class?
Consider the following:
>>>classFoo:...pass...>>>x=Foo()>>>type(x)<class '__main__.Foo'>>>>type(Foo)<class 'type'>The type ofx is classFoo, as you would expect. But the type ofFoo, the class itself, istype. In general, the type of any new-style class istype.
The type of the built-in classes you are familiar with is alsotype:
>>>fortinint,float,dict,list,tuple:...print(type(t))...<class 'type'><class 'type'><class 'type'><class 'type'><class 'type'>For that matter, the type oftype istype as well (yes, really):
>>>type(type)<class 'type'>type is a metaclass, of which classes are instances. Just as an ordinary object is an instance of a class, any new-style class in Python, and thus any class in Python 3, is an instance of thetype metaclass.
In the above case:
x is an instance of classFoo.Foo is an instance of thetype metaclass.type is also an instance of thetype metaclass, so it is an instance of itself.
The built-intype() function, when passed one argument, returns the type of an object. For new-style classes, that is generally the same as theobject’s__class__ attribute:
>>>type(3)<class 'int'>>>>type(['foo','bar','baz'])<class 'list'>>>>t=(1,2,3,4,5)>>>type(t)<class 'tuple'>>>>classFoo:...pass...>>>type(Foo())<class '__main__.Foo'>You can also calltype() with three arguments—type(<name>, <bases>, <dct>):
<name> specifies the class name. This becomes the__name__ attribute of the class.<bases> specifies a tuple of the base classes from which the class inherits. This becomes the__bases__ attribute of the class.<dct> specifies anamespace dictionary containing definitions for the class body. This becomes the__dict__ attribute of the class.Callingtype() in this manner creates a new instance of thetype metaclass. In other words, it dynamically creates a new class.
In each of the following examples, the top snippet defines a class dynamically withtype(), while the snippet below it defines the class the usual way, with theclass statement. In each case, the two snippets are functionally equivalent.
In this first example, the<bases> and<dct> arguments passed totype() are both empty. Noinheritance from any parent class is specified, and nothing is initially placed in the namespace dictionary. This is the simplest class definition possible:
>>>Foo=type('Foo',(),{})>>>x=Foo()>>>x<__main__.Foo object at 0x04CFAD50>>>>classFoo:...pass...>>>x=Foo()>>>x<__main__.Foo object at 0x0370AD50>Here,<bases> is a tuple with a single elementFoo, specifying the parent class thatBar inherits from. An attribute,attr, is initially placed into the namespace dictionary:
>>>Bar=type('Bar',(Foo,),dict(attr=100))>>>x=Bar()>>>x.attr100>>>x.__class__<class '__main__.Bar'>>>>x.__class__.__bases__(<class '__main__.Foo'>,)>>>classBar(Foo):...attr=100...>>>x=Bar()>>>x.attr100>>>x.__class__<class '__main__.Bar'>>>>x.__class__.__bases__(<class '__main__.Foo'>,)This time,<bases> is again empty. Two objects are placed into the namespace dictionary via the<dct> argument. The first is an attribute namedattr and the second a function namedattr_val, which becomes a method of the defined class:
>>>Foo=type(...'Foo',...(),...{...'attr':100,...'attr_val':lambdax:x.attr...}...)>>>x=Foo()>>>x.attr100>>>x.attr_val()100>>>classFoo:...attr=100...defattr_val(self):...returnself.attr...>>>x=Foo()>>>x.attr100>>>x.attr_val()100Only very simple functions can be defined withlambda in Python. In the following example, a slightly more complex function is defined externally then assigned toattr_val in the namespace dictionary via the namef:
>>>deff(obj):...print('attr =',obj.attr)...>>>Foo=type(...'Foo',...(),...{...'attr':100,...'attr_val':f...}...)>>>x=Foo()>>>x.attr100>>>x.attr_val()attr = 100>>>deff(obj):...print('attr =',obj.attr)...>>>classFoo:...attr=100...attr_val=f...>>>x=Foo()>>>x.attr100>>>x.attr_val()attr = 100Consider again this well-worn example:
>>>classFoo:...pass...>>>f=Foo()The expressionFoo() creates a new instance of classFoo. When the interpreter encountersFoo(), the following occurs:
The__call__() method ofFoo’s parent class is called. SinceFoo is a standard new-style class, its parent class is thetype metaclass, sotype’s__call__() method is invoked.
That__call__() method in turn invokes the following:
__new__()__init__()IfFoo does not define__new__() and__init__(), default methods are inherited fromFoo’s ancestry. But ifFoo does define these methods, they override those from the ancestry, which allows for customized behavior when instantiatingFoo.
In the following, a custom method callednew() is defined and assigned as the__new__() method forFoo:
>>>defnew(cls):...x=object.__new__(cls)...x.attr=100...returnx...>>>Foo.__new__=new>>>f=Foo()>>>f.attr100>>>g=Foo()>>>g.attr100This modifies the instantiation behavior of classFoo: each time an instance ofFoo is created, by default it is initialized with an attribute calledattr, which has a value of100. (Code like this would more usually appear in the__init__() method and not typically in__new__(). This example is contrived for demonstration purposes.)
Now, as has already been reiterated, classes are objects too. Suppose you wanted to similarly customize instantiation behavior when creating a class likeFoo. If you were to follow the pattern above, you’d again define a custom method and assign it as the__new__() method for the class of whichFoo is an instance.Foo is an instance of thetype metaclass, so the code looks something like this:
# Spoiler alert: This doesn't work!>>>defnew(cls):...x=type.__new__(cls)...x.attr=100...returnx...>>>type.__new__=newTraceback (most recent call last): File"<pyshell#77>", line1, in<module>type.__new__=newTypeError:can't set attributes of built-in/extension type 'type'Except, as you can see, you can’t reassign the__new__() method of thetype metaclass. Python doesn’t allow it.
This is probably just as well.type is the metaclass from which all new-style classes are derived. You really shouldn’t be mucking around with it anyway. But then what recourse is there, if you want to customize instantiation of a class?
One possible solution is a custom metaclass. Essentially, instead of mucking around with thetype metaclass, you can define your own metaclass, which derives fromtype, and then you can muck around with that instead.
The first step is to define a metaclass that derives fromtype, as follows:
>>>classMeta(type):...def__new__(cls,name,bases,dct):...x=super().__new__(cls,name,bases,dct)...x.attr=100...returnx...The definition headerclass Meta(type): specifies thatMeta derives fromtype. Sincetype is a metaclass, that makesMeta a metaclass as well.
Note that a custom__new__() method has been defined forMeta. It wasn’t possible to do that to thetype metaclass directly. The__new__() method does the following:
super() to the__new__() method of the parent metaclass (type) to actually create a new classattr to the class, with a value of100Now the other half of the voodoo: Define a new classFoo and specify that its metaclass is the custom metaclassMeta, rather than the standard metaclasstype. This is done using themetaclass keyword in the class definition as follows:
>>>classFoo(metaclass=Meta):...pass...>>>Foo.attr100Voila!Foo has picked up theattr attribute automatically from theMeta metaclass. Of course, any other classes you define similarly will do likewise:
>>>classBar(metaclass=Meta):...pass...>>>classQux(metaclass=Meta):...pass...>>>Bar.attr,Qux.attr(100, 100)In the same way that a class functions as a template for the creation of objects, a metaclass functions as a template for the creation of classes. Metaclasses are sometimes referred to asclass factories.
Compare the following two examples:
Object Factory:
>>>classFoo:...def__init__(self):...self.attr=100...>>>x=Foo()>>>x.attr100>>>y=Foo()>>>y.attr100>>>z=Foo()>>>z.attr100Class Factory:
>>>classMeta(type):...def__init__(...cls,name,bases,dct...):...cls.attr=100...>>>classX(metaclass=Meta):...pass...>>>X.attr100>>>classY(metaclass=Meta):...pass...>>>Y.attr100>>>classZ(metaclass=Meta):...pass...>>>Z.attr100As simple as the above class factory example is, it is the essence of how metaclasses work. They allow customization of class instantiation.
Still, this is a lot of fuss just to bestow the custom attributeattr on each newly created class. Do you really need a metaclass just for that?
In Python, there are at least a couple other ways in which effectively the same thing can be accomplished:
Simple Inheritance:
>>>classBase:...attr=100...>>>classX(Base):...pass...>>>classY(Base):...pass...>>>classZ(Base):...pass...>>>X.attr100>>>Y.attr100>>>Z.attr100Class Decorator:
>>>defdecorator(cls):...classNewClass(cls):...attr=100...returnNewClass...>>>@decorator...classX:...pass...>>>@decorator...classY:...pass...>>>@decorator...classZ:...pass...>>>X.attr100>>>Y.attr100>>>Z.attr100As Tim Peters suggests,metaclasses can easily veer into the realm of being a “solution in search of a problem.” It isn’t typically necessary to create custom metaclasses. If the problem at hand can be solved in a simpler way, it probably should be. Still, it is beneficial to understand metaclasses so that you understandPython classes in general and can recognize when a metaclass really is the appropriate tool to use.
Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding:Metaclasses in Python
🐍 Python Tricks 💌
Get a short & sweetPython Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

AboutJohn Sturtz
John is an avid Pythonista and a member of the Real Python tutorial team.
» More about JohnMasterReal-World Python Skills With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
MasterReal-World Python Skills
With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
What Do You Think?
What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.
Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students.Get tips for asking good questions andget answers to common questions in our support portal.
Keep Learning
Already have an account?Sign-In
Almost there! Complete this form and click the button below to gain instant access:

Join the Real Python Community Newsletter (More Than 45,468 Python Developers Have Subscribed)
Get aPython Cheat Sheet (PDF) and learn the basics of Python, like working with data types, dictionaries, lists, and Python functions:
