Как в Python создать простой метакласс

Ответ

Метакласс в Python — это "класс для классов". Если обычный класс определяет, как создаются и ведут себя объекты (экземпляры), то метакласс определяет, как создаются и ведут себя сами классы.

По умолчанию метаклассом для всех классов является type. Чтобы создать свой, нужно унаследовать класс от type и переопределить его метод __new__ или __init__.

Пример: Метакласс, добавляющий атрибут created_at ко всем классам

import time

# 1. Создаем метакласс, наследуясь от type
class TimestampMeta(type):
    # 2. Переопределяем __new__
    # Этот метод вызывается при создании нового КЛАССА
    def __new__(cls, name, bases, namespace):
        # Добавляем новый атрибут в пространство имен будущего класса
        namespace['created_at'] = time.ctime()
        print(f"Создание класса '{name}' с метаклассом TimestampMeta...")

        # Вызываем __new__ родительского класса (type) для завершения создания
        return super().__new__(cls, name, bases, namespace)

# 3. Применяем метакласс к нашему классу
class MyModel(metaclass=TimestampMeta):
    def __init__(self):
        self.data = "some data"

# Проверяем, что атрибут был добавлен в класс
print(MyModel.created_at)
# > Создание класса 'MyModel' с метаклассом TimestampMeta...
# > [текущее время, например, 'Mon Dec 18 15:30:00 2023']

Разбор метода __new__:

  • cls: Сам метакласс (TimestampMeta).
  • name: Имя создаваемого класса в виде строки ('MyModel').
  • bases: Кортеж родительских классов ((object,)).
  • namespace: Словарь, содержащий все атрибуты и методы, определенные в теле класса ({'__init__': <function...>, '__module__': ...}). Мы можем изменять этот словарь перед созданием класса.

Основные сценарии использования:

  • Автоматическая регистрация: Регистрация плагинов или моделей в реестре (как в Django ORM).
  • Валидация: Проверка, что создаваемый класс соответствует определенным правилам (например, имеет нужные методы).
  • Инъекция атрибутов: Автоматическое добавление логгеров, полей или методов в классы.

Ответ 18+ 🔞

А, метаклассы в Python, ёпта! Ну это ж как высший пилотаж, блядь. Сейчас объясню на пальцах, только не обосрись со смеху.

Представь, обычный класс — это как чертёж для табуретки. По нему штампуешь табуретки-объекты. А метакласс — это, сука, чертёж для самих чертежей! То есть инструкция, как создавать эти самые чертежи-классы. Во какие завороты, блядь!

По дефолту у всех классов метакласс — это type, старый добрый тип. Но если хочешь творить магию на уровне создания классов, нужно наследоваться от type и переопределить методы.

Вот тебе пример: метакласс, который автоматом прикручивает время создания к каждому классу

import time

# 1. Делаем метакласс, наследуясь от type
class TimestampMeta(type):
    # 2. Переопределяем __new__ — это главный колдунский метод
    # Он вызывается, когда создаётся новый КЛАСС, а не объект!
    def __new__(cls, name, bases, namespace):
        # Подсовываем в пространство имён будущего класса новый атрибут
        namespace['created_at'] = time.ctime()
        print(f"Вот я создаю класс '{name}' через метакласс TimestampMeta, бля...")

        # А теперь пусть родительский type довершит дело
        return super().__new__(cls, name, bases, namespace)

# 3. Применяем эту хуйню к своему классу
class MyModel(metaclass=TimestampMeta):
    def __init__(self):
        self.data = "какие-то данные"

# Смотри, что получилось!
print(MyModel.created_at)
# > Вот я создаю класс 'MyModel' через метакласс TimestampMeta, бля...
# > [тут будет время, типа 'Mon Dec 18 15:30:00 2023']

Что за параметры в __new__, блядь:

  • cls: Это сам метакласс, наш TimestampMeta.
  • name: Имя класса, который создаём, в виде строки. Будет 'MyModel'.
  • bases: Кортеж из родительских классов. Обычно (object,).
  • namespace: Словарь, куда сложено всё, что написали в теле класса — методы, атрибуты. Его можно, сука, как хочешь допиливать перед тем, как класс родится!

А где это, блядь, применяется? Да везде, где нужно колдовать над самими классами, а не их экземплярами!

  • Авторегистрация всякой хуйни: Как в Django ORM — написал класс модели, а он сам в реестр записался.
  • Проверки, что класс правильный: Чтобы, например, все нужные методы были на месте, а то пиздец.
  • Автоматическое добавление полей или методов: Подкинуть всем классам логгер, таймстамп или ещё какую-нибудь обязаловку.

В общем, мощнейшая штука, но если бездумно применять — можно так наколдовать, что потом сам не разберёшься, в рот меня чих-пых!