A Summary of Python Class Creation
TL;DR
Python classes have many aspects and can be quite complex, so let me summarize them. First, let's cover class creation.
Class Creation
- First, the
__new__method is called, followed by the__init__method. - In the
__new__method, class variables are defined. In the__init__method, instance variables can be defined. - Class variables are variables accessible by all instances created from the class, while instance variables are variables accessible only from each individual instance.
- Class variables can be used even without creating an instance, and methods that use class variables are marked with the
@classmethoddecorator. - By convention, the created instance is named
self, and the class itself is namedcls.
Verifying the Behavior of __new__ and __init__
We define a class as shown above. @staticmethod is a decorator for methods that do not take cls or self as arguments (i.e., methods that do not access class variables or instance variables). These methods can also be used without creating an instance.
As shown below, the parts defined with @classmethod and @staticmethod can be used without creating an instance.
Next, let's look at the instance creation process.
Looking at the IDs after instance creation, we can see that the ID generated in __new__ is the same, meaning the class itself is being created. The IDs generated in __init__ are different, confirming that different instances are being created.
Let's also verify the behavior when changing class variables.
When the class variable is changed from either side, the class variable is updated for both instances.
What is __new__ Used For?
Use cases for __new__ include:
-
Recording how many times a class has been called
-
Initializing immutable objects such as tuples
-
Switching classes using metaclasses
The official intended use cases appear to be 2 and 3 (Reference: 3. Data Model).
The primary purpose of
__new__()is to allow subclasses of immutable types (like int, str, tuple) to customize instance creation. It is also commonly overridden in custom metaclasses to customize class creation.
Dynamic Class Definition
When type takes a single argument of type object, it returns object.__class__. This allows you to identify a class as follows:
To create a class, type takes three arguments: name, bases, dict. These correspond to __name__, __bases__, __dict__. __bases__ is for inheritance, where () or (object,) represents the most basic class. The correspondence with normal class creation is as follows:
In other words, normal class creation involves instantiating type within __new__. The important thing here is that type is a class (Reference: Built-in Functions). The relationships around this are well explained in A Visual Guide to Python's Objects and Classes -- Everything is an Object.
So, by using a class that inherits from type and calling that class within __new__, you can dynamically customize class creation. This concept is the metaclass.
Practical Examples of Metaclasses
A metaclass is a "class of a class." Just as a regular class defines the behavior of instances, a metaclass controls the creation process of the class itself. You can define a custom metaclass by inheriting from type and overriding __new__.
Basic Metaclass -- Automatic Attribute Addition
As the simplest example, let's define a metaclass that automatically adds attributes during class creation.
By specifying metaclass=AutoAttrMeta, AutoAttrMeta.__new__ is called instead of type.__new__ when the class statement is executed. The argument mcs is the metaclass itself (AutoAttrMeta), name is the class name, bases is a tuple of base classes, and namespace is a dictionary of attributes defined in the class body.
Registry Pattern -- Automatic Class Registration
One of the most commonly used metaclass patterns in practice is automatic class registration (Registry). Simply defining a subclass automatically registers it in a dictionary, which can be used for plugin systems or serialization mechanisms.
In this pattern, defining a new subclass automatically registers it, preventing missed registrations. It is powerful in designs that require extensibility, such as plugin systems and command dispatching.
Singleton Pattern -- Controlling Instance Creation
By overriding __call__ in a metaclass, you can control the instance creation process. While __new__ controls class creation, __call__ controls behavior when that class is called (i.e., when an instance is created).
The important distinction here is the difference in roles between __new__ and __call__ in metaclasses:
__new__: Called when theclassstatement is executed. Creates the class object itself.__call__: Called when a created class is invoked asMyClass(). Controls instance creation.
This distinction is analogous to the relationship between __new__ (instance creation) and __init__ (instance initialization) in regular classes, as explained at the beginning of this article. In metaclasses, the same kind of control happens one level higher.
Summary
Metaclasses are a very powerful feature, but they are rarely needed in everyday Python programming. Since Python 3.6, __init_subclass__ can be used to achieve use cases like the Registry pattern without metaclasses.
Before using metaclasses, it is a good idea to first consider whether your goal can be achieved with decorators or __init_subclass__. If those are not sufficient, then it is time for metaclasses.