Page 1 of 2 Metaclasses are an advanced topic and you generally don’t need to know about them or make use of them .....but knowing how they work might suggest alternative approaches to many problems. This extract is from my new book published on July 9th with the subtitle "Something Completely Different".
Programmer's Python Everything is an Object Second Edition
Is now available as a print book: Amazon
Contents
- Get Ready For The Python Difference
- Variables, Objects and Attributes
- The Function Object
- Scope, Lifetime and Closure
Extract 1: Local and Global ***NEW!
- Advanced Functions
- Decorators
- Class, Methods and Constructors
Extract 1: Objects Become Classes
- Inside Class
- Meeting Metaclasses
- Advanced Attributes
- Custom Attribute Access
- Single Inheritance
- Multiple Inheritance
- Class and Type
- Type Annotation
- Operator Overloading
- Python In Visual Studio Code
Extracts from the first edition
<ASIN:1871962749>
<ASIN:1871962595>
<ASIN:1871962765>
Understanding metaclasses
It is only after you have mastered metaclasses can you truly say that you understand how Python implements objects.
Just as classes create instances so metaclasses create classes.
This is the key point in understanding why and how you might use a metaclass. If you only want a single class with a particular set of behaviors then simply implement it using what you know. If, on the other hand, you want to create a set of classes with a particular behavior then you might need to use a metaclass.
You could say that metaclasses are about organizing classes just as classes are about organizing instances.
It is also worth saying that Python 3 is so flexible that there is usually more than one way of approaching a task. Metaclasses have become slightly less useful since the introduction of decorators which can take a class and transform it into a new class with additional features. Many things that can be done with a metaclass can be done with a decorator and it is often a matter of taste which is considered better. However, there are some things that only a metaclass can achieve and they are still important if not commonly used.
Metaclass Basics
Everything in Python is an object.
Classes are special objects that you can call, i.e. use like a function object, to create new objects.
Classes are object factories.
In the previous chapter we have been looking at how a class creates the instance – this is what __new__ and __init__ are all about.
There is another stage in the life of a class object, however.
When the Python system reads the class definition it has to create a class object. That is, when the Python system encounters a class definition in your program it has to create the class object it defines for you to use later in your program to call to create instances of the class. The class definition creates a class object just as the function def creates a function object.
The simplest approach would be to just get the Python system to create the class object from the class definition as an automatic “behind the scenes” operation but Python provides a mechanism whereby you can modify the way class objects are created.
A metaclass is an object that creates a class.
It is a class object factory in the same way that a class object is an instance object factory.
The default metaclass object is called type.
Every object has a __class__ attribute which is used to record the class that created it.
Instance objects have the class object that created them as their __class__.
By default all class objects have type as their __class__.
So what does the type object have for its __class__?
The answer is type.
The hierarchy has to stop somewhere and Python solves the problem by having type nominally create itself.
Type is a metaclass, the fundamental metaclass.
You can define your own metaclasses, however, by creating a class that inherits from type.
A custom metaclass is just a class that inherits from type.
Class Creation Using Type
How does type do its job and create a class?
When the system encounters a class definition, it first executes the body of the class to discover what attributes are created. These are put into a dictionary as the class’ namespace.
For example:
class MyClass:
myAttribute=21*2
The body of the class is executed resulting in a dictionary/namespace containing:
{‘myAttribute’:42}
Next the metaclass is determined, Meta say, and called as:
Meta(name,bases, dct)
where name is the name of the new class, bases are the base classes of the new class and dct is the namespace dictionary. The call to Meta returns the class object.
The missing piece of the jigsaw is finding the metaclass. This can be specified as part of the class definition:
class MyClass(metaclass=type):
If metaclass= is present in the class definition then the metaclass it specifies is used to create the class object. If it isn’t specified then any metaclass defined on any base classes is used and if there are none, type is used.
For example, in the case of:
class MyClass: myAttribute=21*2
As explained first the body is executed to get a dictionary that acts as the namespace:
ns={"myAttribute":42}
Next the metaclass is determined to be the default type object and this is called using the equivalent of:
MyClass=type("MyClass",(object,),ns)
This creates a class object with the name MyClass, inheriting from object and using the namespace ns i.e. it will have an attribute called myAttribute set to 42.
If you want to you can call type directly and use it to create class objects dynamically.
That is, you can use:
MyClass=type("MyClass",(object,),ns)
as a line in your program as an alternative way to create a class with the attributes and base classes listed.
The above description is for the default metaclass type but the same sequence occurs if you use a custom metaclass. A custom metaclass has to inherit from type – this seems reasonable as you still have to use type to create your class.
For example:
class MyMeta(type):
pass
class MyClass(metaclass=MyMeta): myAttribute=21*2
Now we have a custom metaclasss MyMeta, which doesn’t do anything, and we have set MyClass to have the metaclass MyMeta. If you run this you will discover that MyClass is created as normal. First the body is executed to get the namespace, then the metaclass is determined and the system executes the equivalent of:
MyClass=MyMeta(“MyClass,(object,),ns)
|