С тех пор, как я впервые понял метаклассы в Python, я продолжал задаваться вопросом «что можно сделать с помощью мета-мета-класса?». Это было по крайней мере 10 лет назад - и теперь, всего пару месяцев назад, мне стало ясно, что существует один механизм создания классов Python, который на самом деле включает в себя класс «мета-мета». И, следовательно, можно попытаться представить себе какое-то использование для этого.
Чтобы повторить создание экземпляра объекта в Python: всякий раз, когда создается экземпляр объекта в Python, «вызывая» его класс с тем же синтаксисом, который используется для вызова обычной функции, __new__
и __init__
класса. То, что «управляет» вызовом этих методов в классе, это именно метод класса «метакласс» __call__
. Обычно, когда пишут метакласс в Python, настраивается метод метакласса __new__
или __init__
.
Итак, получается, что, написав класс «мета-мета», можно настроить его метод __call__
и, таким образом, контролировать, какие параметры передаются и метаклассам __new__
и __init__
, а также, если некоторые другие код должен быть вызван до или после тех. В итоге получается, что сами меткалсы обычно жестко закодированы, и нужно всего несколько, если они вообще есть, даже в очень больших проектах. Поэтому любая настройка, которая может быть выполнена при вызове «meta meta», обычно выполняется непосредственно в самом метаклассе.
И они, есть и другие, менее часто используемые метаклассы Python - можно настроить метод __add__
в метаклассе так, чтобы определяемые ими классы были «добавляемыми», и создать производный класс, имеющий два добавленных класса как суперкласса. Этот механизм также отлично работает с метаклассами - поэтому, просто у нас «есть некоторый реальный код», следует пример класса «мета-мета», который позволяет составлять «метаклассы» для класса, просто добавляя их в объявление класса :
class MM(type):
def __add__(cls, other):
metacls = cls.__class__
return metacls(cls.__name__ + other.__name__, (cls, other), {})
class M1(type, metaclass=MM):
def __new__(metacls, name, bases, namespace):
namespace["M1"] = "here"
print("At M1 creation")
return super().__new__(metacls, name, bases, namespace)
class M2(type, metaclass=MM):
def __new__(metacls, name, bases, namespace):
namespace["M2"] = "there"
print("At M2 creation")
return super().__new__(metacls, name, bases, namespace)
И мы видим, что работаем на интерактивной консоли:
In [22]: class Base(metaclass = M1 + M2):
...: pass
...:
At M1 creation
At M2 creation
Обратите внимание, что поскольку разные метаклассы в Python, как правило, трудно комбинировать, это на самом деле может быть полезно, позволяя комбинировать метакласс, созданный пользователем, с библиотекой или классом stdlib, без необходимости явно объявлять его как родительский для класса. бывший:
In [23]: import abc
In [24]: class Combined(metaclass=M1 + abc.ABCMeta):
...: pass
...:
At M1 creation