Ответ 1
with_metaclass()
- это функция фабрики служебных классов, предоставляемая библиотекой six
, чтобы упростить разработку кода для Python 2 и 3.
Для временного метакласса используется небольшой запас руки (см. ниже), чтобы присоединить метакласс к обычному классу таким образом, чтобы он был совместим как с Python 2, так и с Python 3.
Цитирование из документации:
Создайте новый класс с базовым классом базы и метаклассом метакласса. Это предназначено для использования в объявлениях классов следующим образом:
from six import with_metaclass class Meta(type): pass class Base(object): pass class MyClass(with_metaclass(Meta, Base)): pass
Это необходимо, поскольку синтаксис для присоединения метакласса изменился между Python 2 и 3:
Python 2:
class MyClass(object):
__metaclass__ = Meta
Python 3:
class MyClass(metaclass=Meta):
pass
Функция with_metaclass()
использует тот факт, что метаклассы а) наследуются подклассами, и б) метакласс может использоваться для генерации новых классов и в) когда вы создаете подкласс из базового класса с метаклассом, создавая фактический объект подкласса делегируется метаклассу. Он эффективно создает новый временный базовый класс с временным метаклассом metaclass
, который при использовании для создания подкласса заменяет временный базовый класс и комбинированный метакласс с выбранным метаклассом:
def with_metaclass(meta, *bases):
"""Create a base class with a metaclass."""
# This requires a bit of explanation: the basic idea is to make a dummy
# metaclass for one level of class instantiation that replaces itself with
# the actual metaclass.
class metaclass(type):
def __new__(cls, name, this_bases, d):
return meta(name, bases, d)
@classmethod
def __prepare__(cls, name, this_bases):
return meta.__prepare__(name, bases)
return type.__new__(metaclass, 'temporary_class', (), {})
Разбиваем вышеперечисленное:
type.__new__(metaclass, 'temporary_class', (), {})
использует метаклассmetaclass
для создания нового объекта класса с именемtemporary_class
, который в противном случае будет полностью пустым.type.__new__(metaclass, ...)
используется вместоmetaclass(...)
, чтобы избежать использования специальной реализацииmetaclass.__new__()
, которая необходима для того, чтобы на следующем шаге сработало.- Только в Python 3, когда
temporary_class
используется в качестве базового класса, Python сначала вызываетmetaclass.__prepare__()
(передавая имя производного класса,(temporary_class,)
в качестве аргументаthis_bases
. Затем предполагаемый метаклассmeta
используется для вызовитеmeta.__prepare__()
, игнорируяthis_bases
и передавая аргументbases
. - затем, после использования возвращаемого значения
metaclass.__prepare__()
в качестве базового пространства имен для атрибутов класса (или просто использования простого словаря в Python 2), Python вызываетmetaclass.__new__()
для создания фактического класса. Это снова передается(temporary_class,)
как кортежthis_bases
, но приведенный выше код игнорирует это и вместо этого используетbases
, вызываяmeta(name, bases, d)
для создания нового производного класса.
В результате использование with_metaclass()
дает вам новый объект класса без дополнительных базовых классов:
>>> class FooMeta(type): pass
...
>>> with_metaclass(FooMeta) # returns a temporary_class object
<class '__main__.temporary_class'>
>>> type(with_metaclass(FooMeta)) # which has a custom metaclass
<class '__main__.metaclass'>
>>> class Foo(with_metaclass(FooMeta)): pass
...
>>> Foo.__mro__ # no extra base classes
(<class '__main__.Foo'>, <type 'object'>)
>>> type(Foo) # correct metaclass
<class '__main__.FooMeta'>