32.7 __class_getitem__ and Generic Classes

In Python, the __class_getitem__ special method is a cornerstone for creating generic classes—classes that are parameterized by one or more types. Introduced in Python 3.7 via PEP 560, it provides a mechanism for classes to support square-bracket notation ([]) for type hinting purposes without immediately creating a new class or metaclass. This method is fundamentally different from __getitem__ which operates on instances; __class_getitem__ is called on the class itself. The Purpose of class_getitem The primary purpose of __class_getitem__ is to enable type parameterization for static type checkers. When you write list[int], you are not, in fact, creating a new type of list at runtime. For the Python interpreter, this operation is largely ornamental. Its real consumer is a static type checker like mypy or Pyright. These tools understand that list[int] signifies a list where all elements are of type int. The __class_getitem__ method allows a class to define what should be returned when this subscripting syntax is used on it, enabling the rich ecosystem of generic type hints we have today.

32.6 class __init_subclass__: A Lighter Alternative to Metaclasses

While metaclasses provide immense power for controlling class creation, they introduce significant complexity and can be challenging to maintain. Python 3.6 introduced __init_subclass__ as a simpler, more readable alternative for many common use cases that previously required a custom metaclass. This hook allows a class to participate in the creation of its subclasses without the full machinery of a metaclass. How init_subclass Works The __init_subclass__ method is automatically called by Python’s built-in type metaclass whenever a class that defines it is subclassed. Unlike a metaclass’s __new__ or init__ methods, which are called for every class being created, __init_subclass__ is only called on the direct parent class of the new subclass. This makes its behavior more intuitive and localized.

32.5 Practical Metaclass Use Cases: ORMs, APIs, Registries

Implementing an Object-Relational Mapping (ORM) Framework Metaclasses provide the foundational mechanism for ORMs to seamlessly bridge the gap between object-oriented Python code and relational database tables. The core function of the metaclass is to intercept class creation, inspect the class attributes, and transform user-defined class fields into descriptors that handle database operations. This allows developers to define their data models using intuitive Python classes while the metaclass handles the complex SQL generation and data conversion logic in the background.

32.4 Metaclass Inheritance and Conflicts

When dealing with metaclass inheritance, Python follows a specific and often non-intuitive resolution order to determine which metaclass to use for a new class. This process is crucial to understand because a metaclass conflict can prevent a class from being created entirely. The resolution algorithm is designed to ensure consistency; a class cannot have two different metaclasses that are not in a subclass relationship, as this would create an ambiguous situation for how the class should be constructed.

32.3 Writing a Custom Metaclass

At its core, a custom metaclass is a class that inherits from type. Its power lies in its ability to intercept and modify the process of class creation. This happens automatically when you define a class and set its metaclass attribute (or inherit from a class that has one). The process is governed by the metaclass’s __new__ and __init__ methods, which are called after the class body has been parsed but before the class object is fully instantiated and bound to its name. The __new__ method is responsible for creating and returning the new class object, while __init__ is responsible for initializing the newly created class. This allows you to inspect, modify, add, or even remove class attributes and methods before the class is finalized.

32.2 How Python Creates a Class: __prepare__, __new__, __init__

At the heart of Python’s class creation mechanism lies the type constructor. The familiar class keyword is syntactic sugar for a call to type(name, bases, dict). However, when a metaclass is involved, this process becomes a meticulously orchestrated three-step dance involving the special methods __prepare__, __new__, and __init__. Understanding this sequence is paramount to mastering advanced metaprogramming in Python. The Role of __prepare__ The process begins with __prepare__, a class method (though declared with @classmethod only when necessary on a regular class) that is called even before the class body is executed. Its sole purpose is to create and return the namespace object that will be used to collect all the attributes (methods, class variables, etc.) defined within the class body.

32.1 Classes Are Objects: type() at Runtime

In Python, classes are not just compile-time blueprints; they are first-class objects created and manipulated at runtime. This is a foundational concept for understanding metaclasses. The built-in type() function is the primary mechanism behind this dynamic class creation. When used with a single argument, type(obj) returns the class (type) of that object. However, when used with three arguments, type(name, bases, dict) dynamically constructs and returns a new class object. This dual nature is key: type is both the function that reveals an object’s type and the class (the metaclass) from which all types, including object and itself, are instantiated.

— joke —

...