Оператор class
является декларативным синтаксисом для вызова метакласса.
class Foo(Bar, metaclass=SomeMeta): # If not specified, the metaclass is type
...
эквивалентен
Foo = SomeMeta('Foo', (Bar,) {...})
, где {...}
- некоторое отображение, построенное изтело заявления class
. Просто в качестве простого примера, не вдаваясь в подробности:
class Foo(Bar, metaclass=SomeMeta):
some_attr = "foo string"
def __init__(self, x=9):
self.y = x = 3
определит имя функции __init__
, а затем передаст {'__init__': __init__, 'some_attr': 'foo string'}
в SomeMeta
. Имя __init__
существует только во временном пространстве имен, созданном оператором class
, не затрагивая любое имя __init__
, которое может существовать вне оператора.
Чтобы ответить на ваш вопрос, иногда оно более гибкое илипроще создать третий аргумент метакласса самостоятельно, чем позволить выражению class
сделать это за вас. То же самое касается второго аргумента. Аргумент first фиксируется синтаксисом оператора class
, но может генерироваться во время выполнения, если вы сами вызываете метакласс.
Обратите внимание, что это означает, что выможет действительно злоупотреблять выражением class
. EnumMeta
из модуля enum
, на мой взгляд, растягивает то, что вы должны делать с одним. Вот действительно оскорбительный пример. На самом деле метакласс не должен быть типом;он просто должен вызываться и должен принимать три позиционных аргумента. Он также может принимать дополнительные ключевые аргументы. Затем он может делать с этими аргументами все, что захочет, и возвращать все, что захочет.
def FooMeta(name, bases, dct, msg):
print(f'{msg} from {name}')
return 3
class Foo(metaclass=FooMeta, msg="hi"):
pass
Foo
даже не класс;это связано с 3
.