← Назад к вопросам
Зачем нужен метакласс (metaclass)?
2.8 Senior🔥 101 комментариев
#Python Core#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Зачем нужен метакласс (metaclass)
Метакласс - это класс для классов. Если объекты - это экземпляры класса, то классы - это экземпляры метакласса. Метаклассы используются редко, но для некоторых задач они необходимы.
Основная идея
# Обычный класс
class Dog:
pass
dog = Dog() # dog - экземпляр класса Dog
# Метакласс - класс для класса
class Meta(type):
pass
class Cat(metaclass=Meta):
pass
cat = Cat() # cat - экземпляр класса Cat
# Cat - экземпляр метакласса Meta
Методы метакласса
class Meta(type):
# __new__ - создание класса
def __new__(mcs, name, bases, namespace):
print(f"Создаю класс {name}")
return super().__new__(mcs, name, bases, namespace)
# __init__ - инициализация класса
def __init__(cls, name, bases, namespace):
print(f"Инициализирую класс {name}")
super().__init__(name, bases, namespace)
# __call__ - вызов класса (создание экземпляра)
def __call__(cls, *args, **kwargs):
print(f"Создаю экземпляр {cls.__name__}")
return super().__call__(*args, **kwargs)
class MyClass(metaclass=Meta):
pass
obj = MyClass() # Срабатывают все три метода
Практический пример 1: Singleton (одиночка)
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=SingletonMeta):
def __init__(self):
self.connection = "MySQL Connection"
db1 = Database()
db2 = Database()
print(db1 is db2) # True - один и тот же объект
Практический пример 2: ORM - автоматическая регистрация
class ModelMeta(type):
registry = {}
def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)
if name != 'Model': # Не регистрируем базовый класс
mcs.registry[name] = cls
return cls
class Model(metaclass=ModelMeta):
pass
class User(Model):
table = "users"
fields = ["id", "name"]
class Post(Model):
table = "posts"
fields = ["id", "title"]
print(ModelMeta.registry) # {'User': <class User>, 'Post': <class Post>}
Практический пример 3: Валидация методов
class ValidatorMeta(type):
def __new__(mcs, name, bases, namespace):
for key, value in namespace.items():
if callable(value) and not key.startswith('_'):
if not hasattr(value, '__doc__') or not value.__doc__:
raise ValueError(f"Метод {key} должен иметь docstring")
return super().__new__(mcs, name, bases, namespace)
class MyAPI(metaclass=ValidatorMeta):
def get_user(self):
"""Получить пользователя""" # OK
pass
def delete_user(self): # Ошибка!
pass
Практический пример 4: Автоматический repr
class AutoReprMeta(type):
def __new__(mcs, name, bases, namespace):
if '__repr__' not in namespace:
def __repr__(self):
attrs = ', '.join(f"{k}={getattr(self, k)}"
for k in self.__dict__)
return f"{name}({attrs})"
namespace['__repr__'] = __repr__
return super().__new__(mcs, name, bases, namespace)
class Person(metaclass=AutoReprMeta):
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("Alice", 30)
print(p) # Person(name=Alice, age=30)
Когда использовать метаклассы
- Singleton - одиночка паттерн
- ORM - SQLAlchemy, Django ORM используют метаклассы
- Фреймворки - автоматическая регистрация, валидация
- Декораторы на уровне класса - масштабируемо
- Проверка контрактов - валидация интерфейсов
Когда НЕ использовать
- Если можно решить через обычное наследование
- Если можно использовать декоратор функции
- Если код станет сложнее для понимания
Альтернативы
# Вместо метакласса - можно использовать __init_subclass__
class Model:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
print(f"Регистрирую класс {cls.__name__}")
class User(Model): # Сработает __init_subclass__
pass
Правило
"Метаклассы - это огонь. Если вы думаете, что нужен метакласс, то на 99% вам он не нужен." (Тим Петерс, создатель Python)
Используй метаклассы только если ты точно знаешь, что делаешь, и более простые решения не подходят.