Создать класс динамически, не создавая его экземпляров - нет метаклассов?
Используя классы определения формы WTForms в качестве примера:
class RegistrationForm(Form):
username = StringField('Username', [validators.Length(min=4, max=25)])
email = StringField('Email Address', [validators.Length(min=6, max=35)])
accept_rules = BooleanField('I accept the site rules', [validators.InputRequired()])
и, глядя на источник библиотеки, кажется, что WTForms позволяет пользователю определить очень простой класс настраиваемой структуры формы (как описано выше), который, в свою очередь, затем используется для создания нового класса поля, который не создается, когда класс генерироваться.
Я прочитал несколько учебных пособий о фабриках классов и метаклассах, и общее мнение состоит в том, чтобы избегать метаклассов и использовать вместо них такие вещи, как декораторы классов. Проблема в том, что учебники либо начнут импортировать дополнительные библиотеки, например: import six
смешивайте объяснения разных версий Python, используйте слишком сложные примеры или советуйте вообще не использовать метаклассы.
Пожалуйста, кто-нибудь может дать очень простое объяснение (для Python 3), как использовать простое определение класса (как пример WTForms выше) вместе с метаклассами для настройки совершенно новой конструкции класса без фактического создания экземпляра класса при его создании.
Редактировать: Извинения за то, что мне трудно объяснить, какова моя конечная цель, но, поскольку я прошел учебники, было неясно, являются ли декораторы классов, метаклассы, магические методы (call, new, init) или их комбинацией такими, какими я являюсь. нужно для достижения того, что я визуализировал, или если то, что я визуализировал, было неправильным способом делать вещи. К сожалению, кажется невозможным судить о том, была ли моя цель неправильной, если я не мог понять механизмы, необходимые для ее достижения. Я понял, что метаклассы - это путь, и мне нужно просто указать правильное направление для очень простого примера метакласса, сделанного на Python 3.x.
1 ответ
Вы можете создавать классы динамически - без пользовательских метаклассов и без декораторов, что выглядит для программиста простым вызовом функции.
Просто позвоните в Python type
с тремя параметрами: имя класса, кортеж с его базами и объект отображения с его пространством имен (т. е. словарь, содержащий атрибуты и методы, которые вы обычно определяете в теле класса).
def __init__(self):
...
namespace = {
'__init__': init,
'name': 'default name'
}
MyClass = type("MyClass", (object,), namespace)
Вы теряете некоторые функции, которые возможны только из-за того, что компилятор делает пару специальных вещей во время построения функций, объявленных в теле класса - в основном, возможность использовать без параметров super
и название искажения атрибутов, начиная с __
, но это все.
Тем не менее, следует отметить, что это не "без метаклассов". "Тип" сам по себе является метаклассом - метаклассом Python по умолчанию для всех объектов - и вызывает метакласс, который создает класс. Нет другого способа создать класс. "Декоратор класса" - это просто метод, который может вносить изменения в объект класса после его создания.
Любая функция или метод, которые выдают новый динамический класс, будут иметь внутри него вызов type
или другой метакласс. В том же духе "метакласс" не создает динамические классы сам по себе - его нужно либо использовать в объявлении тела класса, либо вызывать с (по крайней мере) теми же параметрами, которые использовались для вызова type
,
Что касается рекомендаций для "декораторов классов" вместо метаклассов, я не уверен, что это так (за исключением того факта, что "декоратор классов" не может сам создавать классы динамически): их главный недостаток в том, что не существует обычных способ для подклассов украшенных классов, чтобы декораторы класса родителя применялись к себе автоматически, в то время как метаклассы наследуются.
В Python 3.6 у вас есть __init_subclass__
протокол, который, да, может избежать многих традиционных применений метакласса (но, тем не менее, он не будет "создавать классы динамически" - вызов type
делает это).