I am trying to type the __new__
method in a metaclass in Python so that it pleases mypy. The code would be something like this (taken from pep-3115 - "Metaclasses in Python 3000" and stripped down a bit):
from __future__ import annotations
from typing import Type
# The metaclass
class MetaClass(type):
# The metaclass invocation
def __new__(cls: Type[type], name: str, bases: tuple, classdict: dict) -> type:
result = type.__new__(cls, name, bases, classdict)
print('in __new__')
return result
class MyClass(metaclass=MetaClass):
pass
With this, mypy complains, Incompatible return type for "__new__" (returns "type", but must return a subtype of "MetaClass")
, pointing at the line def __new__
.
I have also tried with:
def __new__(cls: Type[MetaClass], name: str, bases: tuple, classdict: dict) -> MetaClass:
Then mypy complains (about the return result
line): Incompatible return value type (got "type", expected "MetaClass")
.
I have also tried with a type var (TSubMetaclass = TypeVar('TSubMetaclass', bound='MetaClass')
) and the result is the same as using MetaClass
.
Using super().__new__
instead of type.__new__
gave similar results.
What would be the correct way to do it?
To create your own metaclass in Python you really just want to subclass type . A metaclass is most commonly used as a class-factory. When you create an object by calling the class, Python creates a new class (when it executes the 'class' statement) by calling the metaclass.
Python Classes can be Created Dynamically We have seen that everything in Python is an object, these objects are created by metaclasses. Whenever we call class to create a class, there is a metaclass that does the magic of creating the class behind the scenes.
First, the return type is MetaClass
, not type
. Second, you need to explicitly cast the return value, since type.__new__
doesn't know it is returning an instance of MetaClass
. (Its specific return type is determined by its first argument, which isn't known statically.)
from __future__ import annotations
from typing import Type, cast
# The metaclass
class MetaClass(type):
# The metaclass invocation
def __new__(cls: Type[type], name: str, bases: tuple, classdict: dict) -> MetaClass:
result = type.__new__(cls, name, bases, classdict)
print('in __new__')
return cast(MetaClass, result)
class MyClass(metaclass=MetaClass):
pass
To use super
, you need to adjust the static type of the cls
parameter.
class MetaClass(type):
# The metaclass invocation
def __new__(cls: Type[MetaClass], name: str, bases: tuple, classdict: dict) -> MetaClass:
result = super().__new__(name, bases, classdict)
print('in __new__')
return cast(MetaClass, result)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With