Как и любая другая форма определения вложенного класса, вложенный метакласс может быть более «компактным и удобным» (при условии, что вы не используете повторное использование этого метакласса, кроме как по наследованию) для многих видов «производственного использования», но может быть несколько неудобным для отладки и самоанализа.
В принципе, вместо того чтобы дать Метакласс надлежащего высокого уровня, имя, вы будете в конечном итоге с все пользовательские метаклассы, определенные в модуле быть 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 лет спустя (хотя, возможно, код, который вы написали примерно в то же время " для веков "совершенно забыто ;-). Это, несомненно, предполагает отказ от вложенного определения метаклассов.
Очень хорошее объяснение, спасибо. – cji
Впечатляющий ответ. Немного отсутствует в «вы собираетесь полагаться ??? и сериализации». –
Я не уверен, какая проблема - «рассол». Травление, похоже, отлично работает [здесь] (http://gist.github.com/524744). –