Я хочу спросить, что означает вызов with_metaclass()
в определении класса.
например:.
class Foo(with_metaclass(Cls1, Cls2)):
- Это особый случай, когда класс наследуется от метакласса?
- Является ли новый класс также метаклассом?
Я хочу спросить, что означает вызов with_metaclass()
в определении класса.
например:.
class Foo(with_metaclass(Cls1, Cls2)):
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__()
, которая необходима для того, чтобы на следующем шаге сработало.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'>
UPDATE: функция six.with_metaclass()
с тех пор была исправлена с помощью варианта декоратора, то есть @six.add_metaclass()
. Это обновление устраняет некоторые проблемы с mro, связанные с базовыми объектами. Новый декоратор будет применяться следующим образом:
import six
@six.add_metaclass(Meta)
class MyClass(Base):
pass
Вот заметки патча и вот аналогичный, подробный пример и объяснение для использования альтернативы декоратора.