Ответ
Метакласс в Python — это «класс для класса». Он определяет, как создаётся сам класс, точно так же, как обычный класс определяет, как создаются его экземпляры (объекты).
По умолчанию в Python метаклассом для всех классов является type. Создавая собственный метакласс, вы перехватываете процесс создания класса (__new__) и можете изменить его до того, как он будет окончательно сформирован.
Основные сценарии использования:
- Автоматическая регистрация классов: Например, для создания реестра плагинов, ORM-моделей или сериализаторов. При определении нового класса метакласс может автоматически добавить его в глобальный словарь.
- Валидация структуры класса: Проверка на наличие обязательных методов или атрибутов при создании класса.
- Модификация класса: Автоматическое добавление или изменение методов и атрибутов во всех классах, использующих данный метакласс.
Пример: метакласс, требующий наличия атрибута __tablename__
# Определяем метакласс, наследуясь от type
class ModelMeta(type):
def __new__(cls, name, bases, dct):
# cls: сам метакласс (ModelMeta)
# name: имя создаваемого класса ('User')
# bases: кортеж базовых классов
# dct: словарь атрибутов и методов класса
if '__tablename__' not in dct:
raise TypeError(f"Класс {name} должен определять атрибут '__tablename__'")
# Вызываем __new__ родительского метакласса (type), чтобы создать класс
return super().__new__(cls, name, bases, dct)
# Используем метакласс
class User(metaclass=ModelMeta):
__tablename__ = 'users'
id: int
name: str
# Этот код вызовет ошибку TypeError, так как атрибут отсутствует
# class Order(metaclass=ModelMeta):
# order_id: int
Метаклассы — это мощный, но сложный инструмент. В большинстве случаев их можно заменить более простыми механизмами, такими как декораторы классов или наследование.
Ответ 18+ 🔞
А, метаклассы в Python, ну это же просто пиздец, какой-то высший пилотаж, блядь! Это как если бы ты был обычным слесарем, а тут тебе говорят: «А теперь, дружок, смастери-ка станок, который будет делать другие станки». Вот метакласс — это и есть такой станкостроительный станок, ёпта.
По дефолту у всех классов в Питоне метакласс — это type. Ну, типа, стандартный заводик по производству классов. А когда ты свой метакласс пишешь, ты как бы этот заводик захватываешь и начинаешь там свои порядки наводить. «Ага, — говоришь, — все станки, которые тут будут сделаны, должны иметь вот такую хрень на боку!».
Зачем это, спросишь? Ну, например:
- Автоматическая запись в список: Чтоб все классы-плагины сами собой в общий список записывались, как только их объявили. Удобно, блядь, не надо руками втискивать.
- Проверка, что всё на месте: Чтобы прямо при создании класса проверить, а не забыл ли мудак-разработчик обязательный метод написать. И если забыл — сразу ему по ебалу ошибкой!
- Допиливание классов на конвейере: Чтобы ко всем классам одной категории автоматом какой-нибудь метод-костыль приделывался.
Смотри, как это выглядит на практике. Допустим, мы хотим, чтобы у всех наших моделей ОБЯЗАТЕЛЬНО было поле __tablename__.
# Вот наш метакласс-надзиратель. Наследуемся от type.
class ModelMeta(type):
def __new__(cls, name, bases, dct):
# Тут мы ловим класс прямо на стадии сборки, блядь.
# name — это имя класса ('User'), bases — от кого наследуется, dct — что внутри.
# И вот наша проверка, хитрая жопа.
if '__tablename__' not in dct:
# Если нету нужного атрибута — ВЫХОДА НЕТ, ПИЗДЕЦ.
raise TypeError(f"Эй, ты, {name}! Где твой '__tablename__', а? Создавай заново, мудак!")
# Если всё ок — передаём дальше, пусть стандартный `type` доделает класс.
return super().__new__(cls, name, bases, dct)
# А теперь используем нашего надзирателя.
class User(metaclass=ModelMeta):
__tablename__ = 'users' # Всё чётко, проходи.
id: int
name: str
# А вот этот ушлёпок не пройдёт, и его даже не создадут!
# class Order(metaclass=ModelMeta):
# order_id: int # Где __tablename__, сука?!
Вот и вся магия, блядь. Но, честно говоря, это как ядерная дубинка — мощно, но в 90% случаев можно обойтись чем-то попроще, тем же декоратором для класса. А то так увлечёшься, накодишь метаклассов, а потом сам же от них охуеешь, когда через полгода разбираться придётся.