Вызов встроенного класса type()
может использоваться в качестве динамического эквивалента объявления класса. Ниже приведен пример определения класса с вы- зовом type()
:
def method(self):
return 1
MyClass = type('MyClass', (object,), {'method': method})
Это эквивалентно явному определению класса с ключевым словом class
:
class MyClass:
def method(self):
return 1
Каждый класс, который явно создается таким образом, имеет метакласс type
. Такое поведение по умолчанию можно изменить, добавив именованный аргумент metaclass
:
class ClassWithAMetaclass(metaclass=type):
pass
Значение, предоставляемое в качестве аргумента metaclass
, — это, как правило, еще один объект класса, но может быть любым другим вызываемым объектом, который принимает те же аргументы, что и класс type
, и возвращает другой объект класса. Сигнатура вызова такова: type(name, bases, namespace)
. Значение аргументов выглядит следующим образом:
name
— имя класса, которое будет храниться в атрибуте__name__
;bases
— список родительских классов, которые станут атрибутом__base__
и будут использоваться для построения ПРМ вновь созданного класса;namespace
— пространство имен (отображение) с определениями для тела клас- са, который станет атрибутом__dict__
.
Метаклассы — это своего рода метод __new__()
, но на более высоком уровне определения класса. Несмотря на то что вместо метаклассов можно добавить функции, которые явно вызывают type()
, обычно для этого используется другой класс, наследующий от type
. Общий шаблон для метакласса выглядит следующим образом:
class Metaclass(type):
def __new__(mcs, name, bases, namespace):
return super().__new__(mcs, name, bases, namespace)
@classmethod
def __prepare__(mcs, name, bases, **kwargs):
return super().__prepare__(name, bases, **kwargs)
def __init__(cls, name, bases, namespace, **kwargs):
super().__init__(name, bases, namespace)
def __call__(cls, *args, **kwargs):
return super().__call__(*args, **kwargs)
Аргументы name, bases, namespace
имеют такое же значение, как и в type()
, но все эти четыре метода могут иметь различные цели.
- Метод
__new__(mcs, name, bases, namespace)
отвечает за фактическое создание объекта класса, как и у обычных классов. Первый аргумент является объектом метакласса. В предыдущем примере это был бы просто Metaclass. Обратите внимание, чтоmcs
— общепринятое имя для данного аргумента. - Метод
__prepare__(mcs, name, bases, **kwargs)
создает пустой объект про- странства имен. По умолчанию возвращает пустойdict
, но может возвращать и любой другой тип отображения. Обратите внимание: он не принимаетnamespace
в качестве аргумента, поскольку до вызова пространство имен еще не существует. - Метод
__init__(cls, name, bases, namespace, **kwargs)
не особо популярен в реализации метакласса, но имеет тот же смысл, что и в обычных классах. Он может выполнять дополнительную инициализацию объекта класса, как только тот будет создан с помощью__new__()
. Первый позиционный аргумент теперь называетсяcls
и обозначает уже созданный объект класса (экземпляр метакласса), а не объект метакласса. В момент вызова__init__()
класс уже был создан, и поэтому данный метод не так полезен, как__new__()
. Реализация такого метода очень похожа на использование декораторов класса, но основное отличие состоит в том, что__init__()
будет вызываться для каждого подкласса, а вот декораторы класса для подклассов не вызываются. - Метод
__call__(cls, *arg, **kwargs)
вызывается, когда вызывается экземпляр метакласса. Последний является объектом класса, он вызывается при создании новых экземпляров класса. Метод позволяет переопределить способ создания и инициализации экземпляров класса.
-
exec(object, global, locals)
— позволяет динамически выполнять код Python. Элементobject
должен быть строкой или объектом кода (см. функциюcompile()
), представляющим один оператор или последовательность нескольких. Аргументыglobal
иlocal
— это глобальные и локальные пространства имен для исполняемого кода, которые не являются обязательными. Если они не указаны, то код выполняется в текущем пространстве. Если указаны, тоglobal
должен быть словарем, аlocal
может быть любым объектом отображения, он всегда возвращаетNone
. -
eval(expression, global, locals)
— используется для вычисления данного выражения и возвращает его значение. Похоже наexec()
, ноexpression
— это всего одно выражение Python, а не последовательность операторов. Возвращает значение вычисленного выражения. -
compile(source, filename, mode)
— компилирует источник в объект кода или AST. Исходный код предоставляется в качестве строкового значения в аргументеsource
.filename
— это файл, из которого читается код. Если связанного файла нет (например, потому что он был создан динамически), обычно используется значение<string>
.
Режим
exec
(последовательность операторов),eval
(одно выражение) илиsingle
(один интерактивный оператор, например, в интерактивной сессии Python).