Summary: in this tutorial, you’ll learn about the Python metaclass and understand how Python uses the metaclasses to create other classes.
Introduction to the Python Metaclass
A metaclass is a class that creates other classes. By default, Python uses the type
metaclass to create other classes.
For example, the following defines a Person
class:
class Person: def __init__(self, name, age): self.name = name self.age = age
Code language: Python (python)
When Python executes the code, it uses the type
metaclass to create the Person
class. The reason is that the Person
class uses the type
metaclass by default.
The explicit Person
class definition looks like this:
class Person(object, metaclass=type): def __init__(self, name, age): self.name = name self.age = age
Code language: Python (python)
The metaclass
argument allows you to specify which metaclass class to use to define the class. Therefore, you can create a custom metaclass that has its own logic to create other classes. By using a custom metaclass, you can inject functionality into the class creation process.
Python metaclass example
First, define a custom metaclass called Human
that has the freedom
attribute sets to True
by default:
class Human(type): def __new__(mcs, name, bases, class_dict): class_ = super().__new__(mcs, name, bases, class_dict) class_.freedom = True return class_
Code language: Python (python)
Note that the __new__
method returns a new class or a class object.
Second, define the Person
class that uses the Human
metaclass:
class Person(object, metaclass=Human): def __init__(self, name, age): self.name = name self.age = age
Code language: Python (python)
The Person
class will have the freedom
attribute as shown in the class variables:
pprint(Person.__dict__)
Code language: Python (python)
Output:
mappingproxy({'__dict__': <attribute '__dict__' of 'Person' objects>, '__doc__': None, '__init__': <function Person.__init__ at 0x000001E716C71670>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Person' objects>, 'freedom': True})
Code language: Python (python)
Put it all together.
from pprint import pprint class Human(type): def __new__(mcs, name, bases, class_dict): class_ = super().__new__(mcs, name, bases, class_dict) class_.freedom = True return class_ class Person(object, metaclass=Human): def __init__(self, name, age): self.name = name self.age = age pprint(Person.__dict__)
Code language: Python (python)
Metaclass Parameters
To pass parameters to a metaclass, you use the keyword arguments. For example, the following redefines the Human
metaclass that accepts keyword arguments, where each argument becomes a class variable:
class Human(type): def __new__(mcs, name, bases, class_dict, **kwargs): class_ = super().__new__(mcs, name, bases, class_dict) if kwargs: for name, value in kwargs.items(): setattr(class_, name, value) return class_
Code language: Python (python)
The following uses the Human
metaclass to create a Person
class with the country
and freedom
class variables set to USA
and True
respectively:
class Person(object, metaclass=Human, country='USA', freedom=True): def __init__(self, name, age): self.name = name self.age = age
Code language: Python (python)
Here are Person class variables:
pprint(Person.__dict__)
Code language: Python (python)
Output:
mappingproxy({'__dict__': <attribute '__dict__' of 'Person' objects>, '__doc__': None, '__init__': <function Person.__init__ at 0x0000018A334235E0>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Person' objects>, 'country': 'USA', 'freedom': True})
Code language: Python (python)
Put it all together.
from pprint import pprint class Human(type): def __new__(mcs, name, bases, class_dict, **kwargs): class_ = super().__new__(mcs, name, bases, class_dict) if kwargs: for name, value in kwargs.items(): setattr(class_, name, value) return class_ class Person(object, metaclass=Human, freedom=True, country='USA'): def __init__(self, name, age): self.name = name self.age = age pprint(Person.__dict__)
Code language: Python (python)
When to use metaclasses
Here’s the quote from Tim Peter who wrote the Zen of Python:
Metaclasses are deeper magic that 99% of users should never worry about it. If you wonder whether you need them, you don’t (the people who actually need them to know with certainty that they need them and don’t need an explanation about why).Tim Peter
In practice, you often don’t need to use metaclasses unless you maintain or develop the core of large frameworks such as Django.
Leave a Reply