Какие (конкретные) варианты использования для метаклассов?

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

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

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

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

Я начну:

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

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

Ответ 1

У меня есть класс, который обрабатывает неинтерактивный графический интерфейс, как интерфейс к Matplotlib. Однако иногда хочется сделать интерактивный график. Имея только пару функций, я обнаружил, что мне удалось увеличить количество фигур, вызвать рисование вручную и т.д., Но мне нужно было сделать это до и после каждого вызова по графику. Поэтому, чтобы создать как интерактивную оболочку построения, так и внешнюю оболочку, я обнаружил, что более эффективно это делать с помощью метаклассов, обертывая соответствующие методы, чем делать что-то вроде:

class PlottingInteractive:
    add_slice = wrap_pylab_newplot(add_slice)

Этот метод не успевает за изменениями API и т.д., но тот, который выполняет итерации над атрибутами класса в __init__, прежде чем переназначить атрибуты класса, более эффективен и обновлен:

class _Interactify(type):
    def __init__(cls, name, bases, d):
        super(_Interactify, cls).__init__(name, bases, d)
        for base in bases:
            for attrname in dir(base):
                if attrname in d: continue # If overridden, don't reset
                attr = getattr(cls, attrname)
                if type(attr) == types.MethodType:
                    if attrname.startswith("add_"):
                        setattr(cls, attrname, wrap_pylab_newplot(attr))
                    elif attrname.startswith("set_"):
                        setattr(cls, attrname, wrap_pylab_show(attr))

Конечно, могут быть лучшие способы сделать это, но я нашел, что это эффективно. Конечно, это также можно сделать в __new__ или __init__, но это было решение, которое я нашел наиболее простым.

Ответ 2

Меня недавно задали один и тот же вопрос, и я получил несколько ответов. Я надеюсь, что это нормально, чтобы оживить эту тему, поскольку я хотел бы остановиться на нескольких упомянутых случаях использования и добавить несколько новых.

Большинство метаклассов, которые я видел, делают одну из двух вещей:

  • Регистрация (добавление класса в структуру данных):

    models = {}
    
    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            models[name] = cls = type.__new__(meta, name, bases, attrs)
            return cls
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    Всякий раз, когда вы подклассом Model, ваш класс зарегистрирован в словаре models:

    >>> class A(Model):
    ...     pass
    ...
    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...>,
     'B': <__main__.B class at 0x...>}
    

    Это также можно сделать с помощью декораторов класса:

    models = {}
    
    def model(cls):
        models[cls.__name__] = cls
        return cls
    
    @model
    class A(object):
        pass
    

    Или с явной функцией регистрации:

    models = {}
    
    def register_model(cls):
        models[cls.__name__] = cls
    
    class A(object):
        pass
    
    register_model(A)
    

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

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

    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...> # No B :(
    
  • Рефакторинг (изменение атрибутов класса или добавление новых):

    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            fields = {}
            for key, value in attrs.items():
                if isinstance(value, Field):
                    value.name = '%s.%s' % (name, key)
                    fields[key] = value
            for base in bases:
                if hasattr(base, '_fields'):
                    fields.update(base._fields)
            attrs['_fields'] = fields
            return type.__new__(meta, name, bases, attrs)
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    Всякий раз, когда вы подклассом Model и определяете некоторые атрибуты Field, им вводят их имена (например, для более информативных сообщений об ошибках) и сгруппированы в словарь _fields (для легкой итерации, без необходимости каждый раз проверяйте атрибуты класса и все атрибуты его базовых классов:

    >>> class A(Model):
    ...     foo = Integer()
    ...
    >>> class B(A):
    ...     bar = String()
    ...
    >>> B._fields
    {'foo': Integer('A.foo'), 'bar': String('B.bar')}
    

    Опять же, это можно сделать (без наследования) с помощью декоратора класса:

    def model(cls):
        fields = {}
        for key, value in vars(cls).items():
            if isinstance(value, Field):
                value.name = '%s.%s' % (cls.__name__, key)
                fields[key] = value
        for base in cls.__bases__:
            if hasattr(base, '_fields'):
                fields.update(base._fields)
        cls._fields = fields
        return cls
    
    @model
    class A(object):
        foo = Integer()
    
    class B(A):
        bar = String()
    
    # B.bar has no name :(
    # B._fields is {'foo': Integer('A.foo')} :(
    

    Или явно:

    class A(object):
        foo = Integer('A.foo')
        _fields = {'foo': foo} # Don't forget all the base classes' fields, too!
    

    Несмотря на то, что, наоборот, ваша пропаганда для читаемого и поддерживаемого неметанового программирования, это гораздо более громоздко, избыточно и подвержено ошибкам:

    class B(A):
        bar = String()
    
    # vs.
    
    class B(A):
        bar = String('bar')
        _fields = {'B.bar': bar, 'A.foo': A.foo}
    

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

class Metaclass(type):
    def __new__(meta, name, bases, attrs):
        return type.__new__(meta, 'foo', (int,), attrs)

class Baseclass(object):
    __metaclass__ = Metaclass

class A(Baseclass):
    pass

class B(A):
    pass

print A.__name__ # foo
print B.__name__ # foo
print issubclass(B, A)   # False
print issubclass(B, int) # True

Это может быть полезно в фреймворках для выдачи предупреждений, когда определены классы с похожими именами или деревья неполного наследования, но я не могу думать о причине, связанной с троллированием, чтобы фактически изменить эти значения. Может быть, Дэвид Бэйсли может.

В любом случае, в Python 3, метаклассы также имеют метод __prepare__, который позволяет оценивать тело класса в сопоставлении, отличном от dict, поддерживая при этом упорядоченные атрибуты, перегруженные атрибуты и другие злые классные вещи:

import collections

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return collections.OrderedDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(list(attrs))
        # Do more stuff...

class A(metaclass=Metaclass):
    x = 1
    y = 2

# prints ['x', 'y'] rather than ['y', 'x']

class ListDict(dict):
    def __setitem__(self, key, value):
        self.setdefault(key, []).append(value)

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return ListDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(attrs['foo'])
        # Do more stuff...

class A(metaclass=Metaclass):

    def foo(self):
        pass

    def foo(self, x):
        pass

# prints [<function foo at 0x...>, <function foo at 0x...>] rather than <function foo at 0x...>

Вы можете утверждать, что упорядоченные атрибуты могут быть достигнуты при создании счетчиков, а перегрузка может быть смоделирована с аргументами по умолчанию:

import itertools

class Attribute(object):
    _counter = itertools.count()
    def __init__(self):
        self._count = Attribute._counter.next()

class A(object):
    x = Attribute()
    y = Attribute()

A._order = sorted([(k, v) for k, v in vars(A).items() if isinstance(v, Attribute)],
                  key = lambda (k, v): v._count)

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=None):
        if x is None:
            return self._foo0()
        else:
            return self._foo1(x)

Помимо того, что он намного более уродливый, он также менее гибкий: что, если вы хотите, чтобы упорядоченные атрибуты литерала, такие как целые числа и строки? Что, если None является допустимым значением для x?

Вот творческий способ решения первой проблемы:

import sys

class Builder(object):
    def __call__(self, cls):
        cls._order = self.frame.f_code.co_names
        return cls

def ordered():
    builder = Builder()
    def trace(frame, event, arg):
        builder.frame = frame
        sys.settrace(None)
    sys.settrace(trace)
    return builder

@ordered()
class A(object):
    x = 1
    y = 'foo'

print A._order # ['x', 'y']

И вот творческий способ решить второй:

_undefined = object()

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=_undefined):
        if x is _undefined:
            return self._foo0()
        else:
            return self._foo1(x)

Но это много, MUCH voodoo-er, чем простой метакласс (особенно первый, который действительно тает ваш мозг). Я хочу сказать, что метаклассы выглядят как незнакомые и противоречивые, но вы также можете смотреть на них как на следующий шаг эволюции в языках программирования: вам просто нужно настроить свое мышление. В конце концов, вы, вероятно, могли бы сделать все на C, включая определение структуры с указателями функций и передачу ее в качестве первого аргумента для своих функций. Человек, который впервые видит С++, может сказать: "Что это за волшебство? Почему компилятор неявно передает методы this, но не регулярные и статические функции? Лучше быть явным и подробным о ваших аргументах". Но тогда объектно-ориентированное программирование намного мощнее, когда вы его получите; и, как я полагаю, это..., квази-аспектно-ориентированное программирование. И как только вы понимаете метаклассы, они на самом деле очень простые, так почему бы не использовать их, когда это удобно?

И, наконец, метаклассы - рад, а программирование должно быть забавным. Использование стандартных конструкций программирования и шаблонов дизайна все время скучно и скучно, и препятствует вашему воображению. Живи немного! Вот метаметаклас, только для вас.

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls 
        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

class China(type):
    __metaclass__ = MetaMetaclass

class Taiwan(type):
    __metaclass__ = MetaMetaclass

class A(object):
    __metaclass__ = China

class B(object):
    __metaclass__ = Taiwan

print A._label # Made in China
print B._label # Made in Taiwan

Ответ 3

Цель метаклассов заключается не в замене класса/объекта на метакласс/класс - это для изменения поведения определений классов (и, следовательно, их экземпляров) каким-то образом. Эффективно это изменить поведение оператора класса способами, которые могут быть более полезными для вашего конкретного домена, чем значение по умолчанию. Я использовал их для:

  • Отслеживание подклассов, обычно для регистрации обработчиков. Это удобно при использовании настройки стиля плагина, где вы хотите зарегистрировать обработчик для определенной вещи просто путем подкласса и настройки нескольких атрибутов класса. например. предположим, что вы пишете обработчик для различных музыкальных форматов, где каждый класс реализует соответствующие методы (play/get tags и т.д.) для своего типа. Добавление обработчика для нового типа становится:

    class Mp3File(MusicFile):
        extensions = ['.mp3']  # Register this type as a handler for mp3 files
        ...
        # Implementation of mp3 methods go here
    

    Затем метакласс поддерживает словарь {'.mp3' : MP3File, ... } и т.д. и создает объект соответствующего типа, когда вы запрашиваете обработчик через функцию factory.

  • Изменение поведения. Вы можете захотеть придать особое значение определенным атрибутам, что приведет к изменению поведения, когда они присутствуют. Например, вы можете искать методы с именем _get_foo и _set_foo и прозрачно преобразовывать их в свойства. Как настоящий пример, здесь рецепт, который я написал, чтобы дать больше C-подобных описаний структур. Метакласс используется для преобразования объявленных элементов в строку формата структуры, обработки наследования и т.д. И создания класса, способного справиться с ним.

    Для других примеров в реальном мире взгляните на различные ORM, например sqlalchemy ORM или sqlobject. Опять же, целью является интерпретация defintions (здесь определения столбцов SQL) с определенным значением.

Ответ 4

Начнем с классической цитаты Тима Питера:

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

Сказав это, я (периодически) просматриваю истинные применения метаклассов. Тот, который приходит на ум, находится в Django, где все ваши модели наследуются от моделей. Модел. models.Model, в свою очередь, делает некоторые серьезные магии, чтобы обернуть ваши модели БД с добротой Django ORM. Эта магия происходит в виде метаклассов. Он создает всевозможные классы исключений, классы менеджеров и т.д. И т.д.

См. django/db/models/base.py, класс ModelBase() для начала истории.

Ответ 5

Метаклассы могут быть полезны для построения доменных специфических языков в Python. Конкретными примерами являются Django, SQLObject декларативный синтаксис схем базы данных.

Основной пример из Консервативный метакласс от Ian Bicking:

Метаклассы, которые я использовал, были прежде всего для поддержки своего рода декларативный стиль программирования. Для экземпляр, рассмотрите проверку Схема:

class Registration(schema.Schema):
    first_name = validators.String(notEmpty=True)
    last_name = validators.String(notEmpty=True)
    mi = validators.MaxLength(1)
    class Numbers(foreach.ForEach):
        class Number(schema.Schema):
            type = validators.OneOf(['home', 'work'])
            phone_number = validators.PhoneNumber()

Некоторые другие методы: Ингредиенты для построения DSL в Python (pdf).

Edit (by Ali): Пример этого с использованием коллекций и экземпляров - это то, что я бы предпочел. Важным фактом являются случаи, которые дают вам больше энергии и устраняют причины использования метаклассов. Далее стоит отметить, что ваш пример использует смесь классов и экземпляров, что, безусловно, свидетельствует о том, что вы не можете просто делать все это с помощью метаклассов. И создает действительно неравномерный способ сделать это.

number_validator = [
    v.OneOf('type', ['home', 'work']),
    v.PhoneNumber('phone_number'),
]

validators = [
    v.String('first_name', notEmpty=True),
    v.String('last_name', notEmpty=True),
    v.MaxLength('mi', 1),
    v.ForEach([number_validator,])
]

Это не идеально, но уже есть почти нулевая магия, нет необходимости в метаклассах и улучшена однородность.

Ответ 6

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

Когда несколько классов имеют одинаковое специальное поведение, повторять __metaclass__=X, очевидно, лучше, чем повторять код специального назначения и/или вводить специальные совместно используемые суперклассы.

Но даже с одним специальным классом и без предсказуемого расширения __new__ и __init__ являются более чистым способом инициализации переменных класса или других глобальных данных, чем смешивание специального кода и нормальных операторов def и class в теле определения класса.

Ответ 7

Единственный раз, когда я использовал метаклассы в Python, был при написании оболочки для API Flickr.

Моей целью было очистить сайт flickr api и динамически сгенерировать полную иерархию классов, чтобы разрешить доступ API с использованием объектов Python:

# Both the photo type and the flickr.photos.search API method 
# are generated at "run-time"
for photo in flickr.photos.search(text=balloons):
    print photo.description

Так что в этом примере, поскольку я сгенерировал весь API-интерфейс Python Flickr с веб-сайта, я действительно не знаю определения классов во время выполнения. Возможность динамически генерировать типы была очень полезна.

Ответ 8

Я думал об одном и том же вчера и полностью согласен. Сложности в коде, вызванные попытками сделать его более декларативным, в целом делают кодовую базу сложнее поддерживать, труднее читать и, по-моему, менее pythonic. Обычно также требуется много copy.copy() ing (для сохранения наследования и копирования из класса в экземпляр), и это означает, что вам нужно искать во многих местах, чтобы увидеть, что происходит (всегда смотря с метакласса вверх), что противоречит python зерно также. Я выбрал код formencode и sqlalchemy, чтобы убедиться, что такой декларативный стиль стоит того, и его явно нет. Такой стиль должен быть оставлен дескрипторами (такими как свойство и методы) и неизменяемыми данными. Ruby лучше поддерживает такие декларативные стили, и я рад, что основной язык python не идет по этому маршруту.

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

class test(baseclass_with_metaclass):
    method_maker_value = "hello"

может иметь метакласс, который сгенерировал метод в этом классе со специальными свойствами на основе "привет" (скажем, метод, который добавил "привет" в конец строки). Для удобства обслуживания было бы неплохо убедиться, что вам не нужно писать метод в каждом подклассе, который вы делаете, а все, что вам нужно определить, - method_maker_value.

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

Ответ 9

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

Тем не менее, в Smalltalk и Ruby очень удобно иметь возможность изменять существующий класс, но Python не любит делать это напрямую.

Там отличная статья developerWorks о метаклассификации в Python, которая может помочь. Статья в Википедии также очень хороша.

Ответ 10

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

(написанный язык в щеке, но я видел такое обфускацию. Django - прекрасный пример)

Ответ 11

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

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

Ответ 12

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

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

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

Ответ 13

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

Возможность динамически переписывать всю библиотеку без проблем, чтобы она работала должным образом, как ожидалось, в многопоточном приложении может быть чрезвычайно полезна в некоторых случаях. Модуль safetkinter делает это с помощью метакласса, предоставленного threadbox - события и очереди не нужны.

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

Ответ 14

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

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

Ответ 15

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

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

Я хотел: a) иметь доступ к этим столбцам с помощью синтаксиса model_instance.p012017, b) быть достаточно явным в отношении того, что я делаю, и c) не нужно явно определять десятки полей в модели, поэтому я подклассифицировал SQLAlchemy 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

    ...

Ответ 16

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

Ответ 17

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

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

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

Ответ 18

ответ от @Dan Gittik это круто

примеры в конце могли прояснить многие вещи, я изменил его на python 3 и дал некоторые пояснения:

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls

        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

#China is metaclass and it __new__ method would be changed by MetaMetaclass(metaclass)
class China(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#Taiwan is metaclass and it __new__ method would be changed by MetaMetaclass(metaclass)
class Taiwan(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#A is a normal class and it __new__ method would be changed by China(metaclass)
class A(metaclass=China):
    __metaclass__ = China

#B is a normal class and it __new__ method would be changed by Taiwan(metaclass)
class B(metaclass=Taiwan):
    __metaclass__ = Taiwan


print(A._label)  # Made in China
print(B._label)  # Made in Taiwan

  • все является объектом, поэтому класс является объектом
  • Объект класса создан метаклассом
  • весь класс, унаследованный от типа, является метаклассом
  • метакласс может контролировать создание классов
  • метакласс может контролировать создание метакласса (так что он может зацикливаться вечно)
  • это метапрограммирование... вы можете контролировать систему типов во время выполнения
  • опять же, все является объектом, это единая система, тип создания типа и тип создания экземпляра

Ответ 19

Другой вариант использования - когда вы хотите иметь возможность изменять атрибуты уровня класса и быть уверенным, что это влияет только на объект под рукой. На практике это подразумевает "объединение" фаз создания метаклассов и классов, что приводит к тому, что вам приходится иметь дело только с экземплярами классов их собственного (уникального) вида.

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