Для чего нужны метаклассы Python?

Что можно сделать с метаклассами, которые не могут быть другими?

Алекс Мартелли сказал, что есть задачи, которые не могут быть достигнуты без метаклассов здесь. Метаклассы Python против декораторов класса, которые я хотел бы узнать, какие?

Метаклассы незаменимы, если вы хотите иметь объекты класса (в отличие от экземпляров объектов класса), оснащенных «специальным настраиваемым поведением», поскольку поведение объекта зависит от специальных методов от типа объекта, а тип объекта класса точно синоним метакласса.

Например, если вы хотите, чтобы объект класса X, такой, что «print X» испускает «Время сейчас 8:46 утра» (в 8:46 или, в более общем случае, текущее время), это должно означать, что type(x) ( Метакласса AKA X) имеет специальный пользовательский метод __str__ – и аналогичным образом (с различными применимыми специальными методами), если вы хотите придать смысл выражениям, таким как X + Y где X и Y – оба класса, или X[23] (где X, опять же, является объектом класса) и т. д.

Большинство других задач настройки теперь (в Python 2.6 или лучше) проще реализовать с помощью декоратора класса, который может изменить объект класса сразу после окончания инструкции class . Есть еще несколько случаев, когда это невозможно, потому что изменения должны быть сделаны очень рано, если они будут иметь какой-либо эффект (например, установка или изменение __slots__ ).

В Python 3 метаклассы получают один дополнительный бит полезности: метакласс теперь может дополнительно указать объект сопоставления, который будет заполняться во время выполнения тела оператора class (по умолчанию это обычный dict ). Это позволяет сохранить и использовать порядок привязок имен в теле класса (в то время как нормальный dict теряет порядок), что иногда приятно, когда класс должен иметь «поля» в определенном конкретном порядке (например, для отображения 1: 1 на struct C, строка в файле CSV или таблице БД и т. п.) – в Python 2. * это должно было быть избыточно указано (как правило, с дополнительным атрибутом класса, который является последовательностью и, следовательно, сохраняет порядок), и это функция метаданных Python 3 позволяет удалить избыточность.

Добавьте дополнительную гибкость в программирование:

Но в соответствии с этим программированием Metaclass на Python вам могут не понадобиться (пока)

Метаклассы более глубокие, чем 99% пользователей должны когда-либо беспокоиться. Если вы задаетесь вопросом, нужны ли вам они, вы не делаете (люди, которые действительно нуждаются в них, знают с уверенностью, что они в них нуждаются, и не нуждаются в объяснении о том, почему).

– Python Guru Тим Петерс

Я использую метаклассы с некоторой частотой, и они являются чрезвычайно мощным инструментом для инструментария. Иногда ваше решение проблемы может быть более изящным, меньше кода с ними, чем без.

То, что я чаще всего использую метаклассы, – это пост-обработка атрибутов класса во время создания класса. Например, если необходимо указать атрибут name для объектов (например, как может работать ORM Django):

 class AutonamingType(type): def __init__(cls, name, bases, attrs): for k,v in attrs.iteritems(): if getattr(v, '__autoname__', False): v.name = k class Autonamer(object): __metaclass__ = AutonamingType 

Если у вас есть это как инструмент, и вы используете класс, который должен знать его name прежде чем он сможет do_something() :

 class Foo(object): __autoname__ = True def __init__(self, name=None): self.name = name def do_something(self): if self.name is None: raise ValueError('name is None') # now, do something 

Это может повлиять на остальную часть вашего кода:

 class Bar(object): myfoo1 = Foo('myfoo1') myfoo2 = Foo('myfoo2') myfoo3 = Foo('myfoo3') 

и это:

 class Baaz(Autonamer): myfoo1 = Foo() myfoo2 = Foo() myfoo3 = Foo() 

Таким образом, сокращение дублирования (и вероятность того, что имя переменной и назначенное имя могут выйти из синхронизации).

Если вы ищете примеры использования механизма метакласса, вы можете прочитать исходный код django.forms .

Декларативный стиль определения формы реализуется через метакласс.

Они редко нужны, но полезны в тех местах, где вы хотите добавить поведение к основному поведению объекта – сравнить Aspect Oriented Programming или инструментарий, выполняемый в рамках персистентности, таких как Hibernate.

Например, вам может понадобиться класс, который сохраняется или регистрирует каждый новый объект.

Вероятно, нет ничего, что можно было бы сделать исключительно с метаклассами, но для некоторых людей (включая мой) это интересный инструмент, который вы можете использовать. Просто будьте осторожны, чтобы не злоупотреблять, поскольку это может быть сложно.

Например, я использовал метапрограммирование в недавнем проекте. Это был лист OpenOffice calc, который, используя некоторые макросы pyUNO, генерирует некоторые файлы с информацией. Был один лист, который представляет пользователю информацию для заполнения, а остальные могут использоваться для описания типов элементов и их свойств. Затем пользователь может выбрать количество элементов и тип каждого и сгенерировать файлы. Макрос создаст класс с помощью метапрограммирования, следующего за конфигурацией на каждом листе. Затем пользователь может инициировать каждый класс и генерировать объекты.

Это можно было бы сделать без метапрограммирования, но для меня казалось естественным использовать возможности метапрограммирования для этого.

Взгляните на источники Django – например, для генерации моделей используются метаклассы.

http://code.djangoproject.com/wiki/DynamicModels

Внутри Django использует метаклассы для создания моделей на основе класса, который вы предоставляете в своем исходном коде. Не вдаваясь в слишком много деталей, это означает, что вместо того, чтобы ваши классы были фактическими моделями, Django получает описание вашего класса, которое оно использует для создания модели на своем месте.

Первый комментатор утверждает, что использование метаклассов здесь было «драгоценным камнем», которое помогало ему отслеживать неожиданные ошибки, которые возникали для (многих) «дней».