Другие способы решения проблем с помощью магии
Существует область программирования, где классы зачастую более важны, чем экземпляры. Например, декларативные мини-языки (declarative mini-languages) - это библиотеки Python, программная логика которых выражена непосредственно в объявлении класса. Дэвид рассматривает их в своей статье "Создание декларативных мини-языков" (). В подобных случаях использование метаклассов для воздействия на процесс создания класса может быть весьма эффективным.
Одной из декларативных библиотек, основанных на классах, является gnosis.xml.validity. В этой структуре вы объявляете ряд "классов допустимости" ("validity classes"), которые описывают набор ограничений для допустимых документов XML. Эти объявления очень близки к тем, что содержатся в описаниях типа документа (DTDs). Например, документ "диссертация" может быть сконфигурирован с помощью следующего кода:
Листинг 10. Правила gnosis.xml.validity в simple_diss.py
from gnosis.xml.validity import * class figure(EMPTY): pass class _mixedpara(Or): _disjoins = (PCDATA, figure) class paragraph(Some): _type = _mixedpara class title(PCDATA): pass class _paras(Some): _type = paragraph class chapter(Seq): _order = (title, _paras) class dissertation(Some): _type = chapter
Если попытаться создать экземпляр dissertation без надлежащих подэлементов, возбуждается исключение, описывающее ситуацию; подобное имеет место для каждого подэлемента. Правильные подэлементы будут автоматически сгенерированы из более простых аргументов, если существует только один непротиворечивый способ "достроить" тип до корректного состояния.
Хотя классы допустимости часто (неформально) базируются на предварительно существующем DTD, экземпляры этих классов печатаются как внеконтекстные (unadorned) фрагменты документа XML, например:
Листинг 11. Создание документа с базовым классом допустимости
>>> from simple_diss import * >>> ch = LiftSeq(chapter, ('It Starts','When it began')) >>> print ch <chapter><title>It Starts</title> <paragraph>When it began</paragraph> </chapter>
Используя метакласс для создания классов допустимости, мы можем генерировать DTD из самих объявлений класса (и при этом добавить дополнительный метод в эти классы):
Листинг 12. Использование метаклассов во время импорта модуля
>>> from gnosis.magic import DTDGenerator, \ ... import_with_metaclass, \ ... from_import >>> d = import_with_metaclass('simple_diss',DTDGenerator) >>> from_import(d,'**') >>> ch = LiftSeq(chapter, ('It Starts','When it began')) >>> print ch.with_internal_subset() <?xml version='1.0'?> <!DOCTYPE chapter [ <!ELEMENT figure EMPTY> <!ELEMENT dissertation (chapter)+> <!ELEMENT chapter (title,paragraph+)> <!ELEMENT title (#PCDATA)> <!ELEMENT paragraph ((#PCDATA|figure))+> ]> <chapter><title>It Starts</title> <paragraph>When it began</paragraph> </chapter>
Пакету gnosis.xml.validity ничего неизвестно о DTD и внутренних подмножествах. Эти концепции и возможности всецело представлены метаклассом DTDGenerator без внесения каких-либо изменений в gnosis.xml.validity или simple_diss.py. DTDGenerator не подставляет свой собственный метод .__str__() в классы, которые он создает - вы по-прежнему можете вывести внеконтекстный фрагмент XML - но метакласс мог бы легко модифицировать подобные магические методы.