← Назад к вопросам

Что такое метаклассы в Python?

3.0 Senior🔥 131 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Метаклассы в Python

Метаклассы — это классы, экземпляры которых являются классами. В Python всё является объектом, включая классы. Метакласс определяет, как создаётся и ведёт себя класс, подобно тому, как класс определяет, как создаётся и ведёт себя объект.

Основная идея

# Обычный класс — экземпляр метакласса
class Dog:
    pass

# Dog — это объект, создаваемый метаклассом (по умолчанию type)
print(type(Dog))  # <class 'type'>
print(type(Dog).__name__)  # 'type'

# У Dog есть экземпляры
rex = Dog()
print(type(rex))  # <class '__main__.Dog'>

Таким образом, иерархия:

  • type — метакласс (класс классов)
  • Dog — класс (создан метаклассом type)
  • rex — объект (создан классом Dog)

Встроенный метакласс type

По умолчанию все классы в Python создаются метаклассом type:

# Это синтаксический сахар:
class Person:
    name = "Alice"
    def greet(self):
        return f"Hello, {self.name}"

# Эквивалентно:
Person = type('Person', (), {
    'name': 'Alice',
    'greet': lambda self: f"Hello, {self.name}"
})

person = Person()
print(person.greet())  # Hello, Alice

Создание собственного метакласса

Метакласс должен наследоваться от type и переопределить методы __new__ и/или __init__:

class SingletonMeta(type):
    """Метакласс для реализации паттерна Singleton"""
    _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 = "Connected to DB"

db1 = Database()
db2 = Database()

print(db1 is db2)  # True (один и тот же объект)
print(db1.connection)  # Connected to DB

Пример: метакласс для валидации атрибутов

class ValidatedMeta(type):
    """Метакласс, который проверяет типы атрибутов"""
    
    def __new__(mcs, name, bases, namespace):
        # Проверяем, что все методы имеют аннотации типов
        for attr_name, attr_value in namespace.items():
            if callable(attr_value) and not attr_name.startswith('_'):
                if not hasattr(attr_value, '__annotations__'):
                    raise TypeError(f"Method {attr_name} must have type annotations")
        
        return super().__new__(mcs, name, bases, namespace)

class MyClass(metaclass=ValidatedMeta):
    def valid_method(self) -> str:
        return "OK"
    
    # def invalid_method(self):
    #     return "This will raise TypeError"

Пример: автоматическая регистрация подклассов

class PluginMeta(type):
    """Метакласс для автоматической регистрации подклассов"""
    plugins = {}
    
    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)
        if bases:  # Не регистрируем базовый класс
            mcs.plugins[name] = cls
        return cls

class Plugin(metaclass=PluginMeta):
    def execute(self):
        raise NotImplementedError

class PluginA(Plugin):
    def execute(self):
        return "Plugin A executed"

class PluginB(Plugin):
    def execute(self):
        return "Plugin B executed"

print(PluginMeta.plugins)
# {'PluginA': <class 'PluginA'>, 'PluginB': <class 'PluginB'>}

for plugin_name, plugin_class in PluginMeta.plugins.items():
    print(plugin_name, ":", plugin_class().execute())

Методы метакласса

new — создание нового класса:

class TracingMeta(type):
    def __new__(mcs, name, bases, namespace):
        print(f"Creating class {name}")
        return super().__new__(mcs, name, bases, namespace)

init — инициализация класса:

class InitMeta(type):
    def __init__(cls, name, bases, namespace):
        print(f"Initializing class {name}")
        super().__init__(name, bases, namespace)

call — создание экземпляров класса:

class InstanceTrackingMeta(type):
    def __call__(cls, *args, **kwargs):
        print(f"Creating instance of {cls.__name__}")
        instance = super().__call__(*args, **kwargs)
        return instance

Когда использовать метаклассы

  1. Фреймворки и библиотеки — Django ORM, SQLAlchemy используют метаклассы
  2. Singleton паттерн — гарантирует единственный экземпляр
  3. API для плагинов — автоматическая регистрация компонентов
  4. Декораторы на уровне класса — добавление функциональности ко всему классу
  5. DSL (Domain-Specific Language) — создание специализированных языков

Проблемы и осторожность

  • Сложность и читаемость кода
  • Трудная отладка
  • Производительность
  • Большинство проблем решаются без метаклассов
# Обычно ЛУЧШЕ использовать декоратор класса:
def singleton(cls):
    instances = {}
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return get_instance

@singleton
class Database:
    pass

Выводы

Метаклассы — это мощный инструмент в Python для создания гибких фреймворков и библиотек. Однако они редко требуются в обычном коде. Классическая цитата: "Метаклассы — это волшебство, а волшебство опасно". Сначала попробуйте решить проблему простыми средствами: функциями, декораторами классов или наследованием.