Создание метакласса в Python - PullRequest
0 голосов
/ 12 января 2019

Я написал класс на Python, который наследуется от type. Я думал, что это единственное требование к классу, чтобы его называли метаклассом, но я не определил для него метод __new__. Но при создании экземпляра этого нового класса в качестве метакласса я получил сообщение об ошибке:

TypeError: type.__new__() takes exactly 3 arguments (0 given)

Вот мой код:

class a(type) :
    pass 
c= a()

Теперь, когда оператор класса обрабатывается, то, что вызывается метод __new__ для type, является моим предположением. Это связано с тем, что метакласс по умолчанию для всех классов в Python - type.

Теперь, когда я создаю экземпляр класса a, который я предположил как метакласс, исходя из предположения, что любой класс, унаследованный от (type), является метаклассом, разве это не то же самое, что создание класса? Почему это не должно привести к вызову type.__new__ с правильными аргументами?

Ответы [ 2 ]

0 голосов
/ 14 января 2019

Эта ошибка связана с тем, что вы не уважаете подпись 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, намеревающегося использовать его в качестве метакласса, нет необходимости дополнительно создавать его экземпляр, чтобы сказать, что «это теперь метакласс». Подкласс типа уже может быть метаклассом, и его экземпляры будут классами, которые будут иметь его как метакласс.

0 голосов
/ 12 января 2019

Это не работает:

class a(type) :
    pass 
c = a()

... по той же причине, по которой это не работает:

c = type()

В конце концов, оба делают одно и то же.

Чтобы использовать его как метакласс, сделайте следующее:

>>> class Class(metaclass=a):
...     pass
...
>>> Class
<class '__main__.Class'>
>>> type(Class)
<class '__main__.a'>

Вы также можете создать экземпляр класса напрямую, как вы пытались, но вы должны предоставить правильные аргументы:

AnotherClass = type('AnotherClass', (), {})

YetAnotherClass = a('YetAnotherClass', (), {})
...