Когда встраивать определения метакласса в Python?

Сегодня я столкнулся с удивительным определением метакласса в Python здесь , с эффективно встроенным определением метакласса. is

class Plugin(object):
    class __metaclass__(type):
        def __init__(cls, name, bases, dict):
            type.__init__(name, bases, dict)
            registry.append((name, cls))

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

Дальнейшие аргументы:

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

class DeclarativeMeta(type):
    def __new__(meta, class_name, bases, new_attrs):
        cls = type.__new__(meta, class_name, bases, new_attrs)
        cls.__classinit__.im_func(cls, new_attrs)
        return cls
class Declarative(object):
    __metaclass__ = DeclarativeMeta
    def __classinit__(cls, new_attrs): pass

могло быть записано как

class Declarative(object):  #code not tested!
    class __metaclass__(type):
        def __new__(meta, class_name, bases, new_attrs):
            cls = type.__new__(meta, class_name, bases, new_attrs)
            cls.__classinit__.im_func(cls, new_attrs)
            return cls
    def __classinit__(cls, new_attrs): pass

. Какие-либо другие соображения?

15
задан Muhammad Alkarouri 14 August 2010 в 22:38
поделиться

1 ответ

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

По сути, вместо того, чтобы дать метаклассу правильное имя верхнего уровня, вы в конечном итоге получите все настраиваемые метаклассы, определенные в модуле, которые невозможно отличить друг от друга на основе их __ module __ и __ name __ атрибуты (которые Python использует для формирования их repr , если необходимо). Подумайте:

>>> class Mcl(type): pass
... 
>>> class A: __metaclass__ = Mcl
...
>>> class B:
...   class __metaclass__(type): pass
... 
>>> type(A)
<class '__main__.Mcl'>
>>> type(B)
<class '__main__.__metaclass__'>

IOW, если вы хотите изучить, «какой тип является классом A» (помните, что метакласс - это тип класса), вы получите ясный и полезный ответ - это Mcl в основном модуль.Однако, если вы хотите проверить, «какой тип является классом B», ответ не так уж и полезен: он говорит , что это __ метакласс __ в модуле main , но это даже не так:

>>> import __main__
>>> __main__.__metaclass__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__metaclass__'
>>> 

... там нет такой вещи, на самом деле; это сообщение вводит в заблуждение и не очень полезно ;-).

Представление класса по существу '% s.% S'% (c .__ module__, c .__ name __) - простое, полезное и непротиворечивое правило, но во многих случаях, например, оператор class не является уникальным в области видимости модуля, или вообще не находится в области видимости модуля (а внутри функции или тела класса), или даже не существует (классы, конечно, могут быть построены без ] class , явно вызывая их метакласс), это может вводить в заблуждение (и лучшее решение - избегать, насколько это возможно, этих специфических случаев, за исключением тех случаев, когда с их помощью можно получить существенное преимущество). Например, рассмотрим:

>>> class A(object):
...   def foo(self): print('first')
... 
>>> x = A()
>>> class A(object):
...   def foo(self): print('second')
... 
>>> y = A()
>>> x.foo()
first
>>> y.foo()
second
>>> x.__class__
<class '__main__.A'>
>>> y.__class__
<class '__main__.A'>
>>> x.__class__ is y.__class__
False

с двумя операторами class в той же области, второй повторно связывает имя (здесь A ), но существующие экземпляры относятся к первой привязке имя по объекту, а не по имени - поэтому оба объекта класса остаются, один доступен только через type (или __ class __ атрибут) его экземпляров (если есть - если нет, этот первый объект исчезает) - два класса имеют одинаковое имя и модуль (и, следовательно, одно и то же представление), но являются разными объектами.Классы, вложенные в тела классов или функций или созданные прямым вызовом метакласса (включая тип ), могут вызвать аналогичную путаницу, если когда-либо требуется отладка или интроспекция.

Таким образом, вложение метакласса - это нормально, если вам никогда не понадобится отлаживать или иным образом анализировать этот код, и с ним можно жить, если кто-то так понимает эти причуды (хотя это никогда не будет так удобно, как использование красивого, настоящее имя, конечно - точно так же, как отладка функции, закодированной с помощью лямбда , не может быть так удобна, как отладка функции, кодированной с помощью def ). По аналогии с лямбда vs def вы можете обоснованно утверждать, что анонимное, «вложенное» определение подходит для метаклассов, которые настолько просты, такие простые, что никакая отладка или интроспекция не поможет. когда-либо возможно потребуются.

В Python 3 «вложенное определение» просто не работает - там метакласс должен быть передан как аргумент ключевого слова классу, как в class A (metaclass = Mcl): , поэтому определение __ метакласса __ в теле не имеет никакого эффекта. Я считаю, что это также предполагает, что определение вложенного метакласса в коде Python 2, вероятно, уместно только в том случае, если вы точно знаете, что код никогда не нужно будет переносить на Python 3 (поскольку вы делаете этот порт намного сложнее, и вам потребуется исключить определение метакласса для этой цели) - «одноразовый» код, другими словами, которого не будет через несколько лет, когда какая-то версия Python 3 приобретет огромные, убедительные преимущества скорости, функциональности или третьих лиц. партийная поддержка через Python 2.7 (последняя версия Python 2).

Код, который вы ожидаете, что будет выброшенным, как показывает история вычислительной техники, имеет приятную привычку удивлять вас полностью и остается примерно 20 лет спустя (хотя, возможно, код, который вы написали примерно в при этом "на века" напрочь забыто ;-). Это, безусловно, наводит на мысль избегать вложенных определений метаклассов.

19
ответ дан 1 December 2019 в 03:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: