Эта ошибка связана с тем, что вы не уважаете подпись type
.
Наследование от type
действительно достаточно для использования класса в качестве метакласса, но дело в том, что вам действительно нужно использовать в качестве метакласса.
type
сам по себе имеет «два режима работы: при вызове с 3 позиционными аргументами он создает новый класс. И затем type является метаклассом этого класса. Если вызывается с позиционным аргументом 1 , он вообще не создает новый класс или объект - вместо этого он просто возвращает класс этого объекта.
Но нет смысла вызывать type
с никакими аргументами вообще. И аргументы в режимах выше не являются обязательными. Таким образом, вы получите TypeError
, если вы попытаетесь вызвать тип без каких-либо аргументов - и это не «TypeError, потому что что-то пошло не так с классом типа» - это «TypeError, потому что ваш вызов не соответствует вызываемая подпись ".
Когда вы наследуете от type
и ничего не меняете, ваш класс будет вести себя точно так же, как и исходный type
: вы можете вызывать его с помощью one или three Positional аргументы, и код, отвечающий за работу в любом режиме, находится в type.__new__
.
Теперь, если вы хотите использовать свой класс в качестве метакласса, вы действительно можете вызвать его, но в форме трех аргументов: вы задаете ему новое имя класса, его базы и его атрибуты - которые на самом деле могут быть пустыми, но вы должны передать строку, кортеж и словарь в качестве этих трех аргументов:
class A(type): pass
myclass = A("", (), {})
А теперь A
работает как метакласс для myclass
:
In [16]: type(myclass)
Out[16]: __main__.A
Однако всякий раз, когда кто-то определяет метакласс, более обычным является использование его с именованным аргументом metaclass=
при объявлении тела класса:
In [17]: class MyOtherClass(metaclass=A):
...: pass
...:
In [18]: type(MyOtherClass)
Out[18]: __main__.A
Среда выполнения Python затем скомпилирует это тело класса, и когда bytecod для него будет выполнен, он вызовет ваш метакласс '__new__
(а затем __init__
, а до этого его __prepare__
) метод, так что он работает как метакласс.
Итак, на всякий случай неясно: когда вы наследуете класс из type
, намеревающегося использовать его в качестве метакласса, нет необходимости дополнительно создавать его экземпляр, чтобы сказать, что «это теперь метакласс». Подкласс типа уже может быть метаклассом, и его экземпляры будут классами, которые будут иметь его как метакласс.