При создании подкласса с именованным параметром в заголовке, каким механизмом базовый класс получает этот параметр?
Именованные параметры из заголовка подкласса передаются в метод __init_subclass__ базового класса. Этот метод вызывается автоматически после создания нового класса и позволяет настроить подкласс без отдельного метакласса.
Параметр нужно явно принять и, при необходимости, передать оставшиеся аргументы дальше через super().__init_subclass__(**kwargs).
__init_subclass__ появился в Python 3.6 как стандартный механизм настройки подклассов. До этого подобную логику часто помещали в метакласс, что усложняло код и могло приводить к конфликтам метаклассов при множественном наследовании.
Новый подход отделяет настройку уже созданного класса от полного контроля над процессом создания класса. Для типичных задач — регистрации подклассов, проверки параметров и установки атрибутов — отдельный метакласс обычно не требуется.
Иногда базовый класс должен получать параметры конфигурации непосредственно в объявлении подкласса: например, имя плагина, формат данных или режим работы. Если обработать такой параметр только в __init__, будет поздно: __init__ относится к экземплярам, а не к созданию самого класса.
Неверная обработка параметров приводит к ошибке создания класса, потере конфигурации или конфликтам в иерархии множественного наследования. Особенно важно не поглощать чужие параметры молча и корректно передавать их следующим классам.
При объявлении подкласса Python вызывает __init_subclass__ у базового класса и передаёт ему именованные параметры из заголовка:
Метод получает сам новый класс через cls, поэтому может создавать или проверять атрибуты класса. Параметр format потребляется базовым классом и не передаётся дальше.
**kwargs нужен для кооперативного множественного наследования: каждый класс должен обработать собственные параметры и передать остальные через super(). В конце цепочки object.__init_subclass__ не принимает произвольные неизвестные аргументы, поэтому необработанный параметр обычно приводит к TypeError — это полезно, поскольку выявляет ошибку конфигурации.
Параметры заголовка класса сначала участвуют в работе метакласса. Обычный метакласс на базе type передаёт их в __init_subclass__; пользовательский метакласс может перехватить, изменить или не передать их дальше. Поэтому при совместном использовании метакласса и __init_subclass__ нужно явно соблюдать цепочку вызовов.
В системе плагинов каждый подкласс должен объявлять уникальный строковый идентификатор. Возможны три подхода: проверять идентификатор вручную после объявления класса, использовать декоратор или реализовать метакласс.
Ручная проверка ненадёжна: легко забыть вызвать её для нового плагина. Декоратор прост, но выполняется отдельно от механизма наследования и может быть случайно не применён. Метакласс даёт полный контроль, но избыточен и может конфликтовать с другим метаклассом.
Для такой задачи разумно выбрать __init_subclass__: базовый класс автоматически получает параметр, проверяет его уникальность и добавляет подкласс в реестр. В результате ошибка обнаруживается в момент объявления класса, а реализация остаётся совместимой с большинством обычных иерархий наследования.
1. Что произойдёт, если __init_subclass__ не передаст оставшиеся параметры через super()?
Они будут потеряны для следующих классов в цепочке множественного наследования. Это нарушает кооперативный вызов методов: другой базовый класс может ожидать свой параметр, но никогда его не получит.
2. Вызывает ли __init_subclass__ настройку экземпляра?
Нет. Он вызывается один раз при создании объекта-класса, то есть при объявлении подкласса. Для настройки экземпляров предназначены __new__ и __init__, а __init_subclass__ может задавать общие атрибуты или регистрировать сам класс.
3. Что будет с неизвестным параметром в конце цепочки наследования?
Если он не был обработан ни одним базовым классом, его получит object.__init_subclass__, который не принимает произвольные именованные параметры. В результате создание подкласса завершится TypeError; это предпочтительнее молчаливого игнорирования опечатки или неподдерживаемой настройки.