Какие (конкретные) варианты использования для метаклассов? - PullRequest
96 голосов
/ 24 декабря 2008

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

Я считаю, что вам почти никогда не нужно использовать метаклассы. Зачем? потому что я думаю, что если вы делаете что-то подобное с классом, вам, вероятно, следует делать это с объектом. И небольшой редизайн / рефакторинг в порядке.

Возможность использовать метаклассы заставила многих людей во многих местах использовать классы как некий второсортный объект, что мне просто кажется катастрофическим. Нужно ли заменять программирование на метапрограммирование? К сожалению, добавление декораторов классов сделало его еще более приемлемым.

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

Я начну:

Иногда при использовании стороннего библиотека полезно иметь возможность мутировать класс определенным образом.

(Это единственный случай, о котором я могу подумать, и он не конкретный)

Ответы [ 17 ]

3 голосов
/ 25 декабря 2008

Метаклассы не заменяют программирование! Это просто трюк, который может автоматизировать или сделать некоторые задачи более элегантными. Хорошим примером этого является Pygments библиотека подсветки синтаксиса. У него есть класс с именем RegexLexer, который позволяет пользователю определять набор лексических правил как регулярные выражения в классе. Метакласс используется для превращения определений в полезный парсер.

Они как соль; слишком легко использовать.

3 голосов
/ 25 декабря 2008

Я использовал метаклассы для предоставления некоторых атрибутов классам. Возьмем для примера:

class NameClass(type):
    def __init__(cls, *args, **kwargs):
       type.__init__(cls, *args, **kwargs)
       cls.name = cls.__name__

поместит атрибут name в каждый класс, для которого метакласс будет установлен в значение NameClass.

3 голосов
/ 16 марта 2011

Единственный законный вариант использования метакласса - не давать другим любопытным разработчикам прикасаться к вашему коду. Как только любопытный разработчик освоит метаклассы и начнет возиться с вашими, добавьте еще один или два уровня, чтобы не пускать их. Если это не сработает, начните использовать type.__new__ или, возможно, какую-нибудь схему с использованием рекурсивного метакласса.

(написано языком в щеку, но я видел, что такое запутывание сделано. Джанго - прекрасный пример)

2 голосов
/ 14 августа 2011

Это незначительное использование, но ... одна вещь, для которой я нашел метаклассы полезными, - это вызов функции при создании подкласса. Я кодифицировал это в метакласс, который ищет атрибут __initsubclass__: всякий раз, когда создается подкласс, все родительские классы, которые определяют этот метод, вызываются с __initsubclass__(cls, subcls). Это позволяет создать родительский класс, который затем регистрирует все подклассы в некотором глобальном реестре, выполняет инвариантные проверки для подклассов, когда они определены, выполняет операции позднего связывания и т. Д., И все это без необходимости вручную вызывать функции или создавать собственные метаклассы, которые выполняют каждую из этих отдельных обязанностей.

Имейте в виду, я постепенно осознал, что неявное волшебство этого поведения несколько нежелательно, поскольку это неожиданно, если смотреть на определение класса вне контекста ... и поэтому я отошел от использования этого решения для ничего серьезного, кроме инициализации атрибута __super для каждого класса и экземпляра.

1 голос
/ 25 мая 2014

Кажется, что здесь разрешено законное использование здесь - Перезапись строк документации Python с метаклассом.

1 голос
/ 08 ноября 2013

Мне недавно пришлось использовать метакласс, чтобы помочь декларативно определить модель SQLAlchemy вокруг таблицы базы данных, заполненной данными переписи США от http://census.ire.org/data/bulkdata.html

IRE предоставляет оболочки базы данных для таблиц данных переписи, которые создают целочисленные столбцы в соответствии с соглашением об именах из Бюро переписей p012015, p012016, p012017 и т. Д.

Я хотел: а) иметь возможность доступа к этим столбцам с использованием синтаксиса model_instance.p012017, б) быть достаточно явным в том, что я делал, и в) не нужно явно определять десятки полей в модели, поэтому я создал подкласс DeclarativeMeta для перебора диапазона столбцов и автоматического создания полей модели, соответствующих столбцам:

from sqlalchemy.ext.declarative.api import DeclarativeMeta

class CensusTableMeta(DeclarativeMeta):
    def __init__(cls, classname, bases, dict_):
        table = 'p012'
        for i in range(1, 49):
            fname = "%s%03d" % (table, i)
            dict_[fname] = Column(Integer)
            setattr(cls, fname, dict_[fname])

        super(CensusTableMeta, cls).__init__(classname, bases, dict_)

Затем я могу использовать этот метакласс для определения модели и получить доступ к автоматически перечисляемым полям модели:

CensusTableBase = declarative_base(metaclass=CensusTableMeta)

class P12Tract(CensusTableBase):
    __tablename__ = 'ire_p12'

    geoid = Column(String(12), primary_key=True)

    @property
    def male_under_5(self):
        return self.p012003

    ...
0 голосов
/ 02 сентября 2016

Мне пришлось использовать их один раз для двоичного парсера, чтобы было проще его использовать. Вы определяете класс сообщения с атрибутами полей, присутствующих на проводе. Их нужно было упорядочить так, как им было объявлено, чтобы создать из них окончательный формат проводов. Вы можете сделать это с метаклассами, если вы используете упорядоченный dict пространства имен. Фактически, это в примерах для метаклассов:

https://docs.python.org/3/reference/datamodel.html#metaclass-example

Но в целом: очень внимательно оцените, действительно ли вам нужна дополнительная сложность метаклассов.

...