2010-08-14 3 views
15

Сегодня я столкнулся с удивительным определением метакласса в Python here, с определением метакласса, эффективно встроенным. Соответствующая часть:Когда встроенные определения метакласса в Python?

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

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

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

аргумент один из способов будет то, что создано метаклассом не подлежит повторному использованию в других местах с помощью этой методики. Аргумент счетчика заключается в том, что общий шаблон использования метаклассов определяет метакласс и использует его в одном классе и затем наследует от него. Например, в a conservative metaclass определение

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 

Любые другие соображения?

ответ

19

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

В принципе, вместо того чтобы дать Метакласс надлежащего высокого уровня, имя, вы будете в конечном итоге с все пользовательские метаклассы, определенные в модуле быть undistiguishable друг от друга на основе их __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, если вы хотите, чтобы исследовать «какой тип класса А» (метаклассом является тип Класс, помните), вы получите четкий и полезный ответ - это в основном модуле Mcl. Тем не менее, если вы хотите, чтобы исследовать «какой тип класса B», то ответ не все, что полезно: это говорит это в main модуле __metaclass__, но это даже не так:

>>> 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__) его экземпляров (если есть - если нет, этот первый объект исчезает) - два класса имеют одинаковое имя и модуль (и, следовательно, то же представление), но они разные объекты. Классы, вложенные в тела классов или функций или созданные путем прямого вызова метакласса (включая type), могут вызывать подобное замешательство, если требуется когда-либо вызывать отладку или интроспекцию.

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

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

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

+1

Очень хорошее объяснение, спасибо. – cji

+0

Впечатляющий ответ. Немного отсутствует в «вы собираетесь полагаться ??? и сериализации». –

+0

Я не уверен, какая проблема - «рассол». Травление, похоже, отлично работает [здесь] (http://gist.github.com/524744). –

Смежные вопросы