A metaclass is a class that describes
the construction and behavior of other classes, similarly to how classes
describe the construction and behavior of objects.
The default metaclass is
type, but it’s possible to use other metaclasses.
Metaclasses allows one to create “a different kind of class”, such as
NamedTuples and singletons.
Mypy has some special understanding of
Defining a metaclass#
class M(type): pass class A(metaclass=M): pass
Metaclass usage example#
Mypy supports the lookup of attributes in the metaclass:
from typing import Type, TypeVar, ClassVar T = TypeVar('T') class M(type): count: ClassVar[int] = 0 def make(cls: Type[T]) -> T: M.count += 1 return cls() class A(metaclass=M): pass a: A = A.make() # make() is looked up at M; the result is an object of type A print(A.count) class B(A): pass b: B = B.make() # metaclasses are inherited print(B.count + " objects were created") # Error: Unsupported operand types for + ("int" and "str")
Gotchas and limitations of metaclass support#
Note that metaclasses pose some requirements on the inheritance structure, so it’s better not to combine metaclasses and class hierarchies:
class M1(type): pass class M2(type): pass class A1(metaclass=M1): pass class A2(metaclass=M2): pass class B1(A1, metaclass=M2): pass # Mypy Error: metaclass conflict # At runtime the above definition raises an exception # TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases class B12(A1, A2): pass # Mypy Error: metaclass conflict # This can be solved via a common metaclass subtype: class CorrectMeta(M1, M2): pass class B2(A1, A2, metaclass=CorrectMeta): pass # OK, runtime is also OK
Mypy does not understand dynamically-computed metaclasses, such as
class A(metaclass=f()): ...
Mypy does not and cannot understand arbitrary metaclass code.
Mypy only recognizes subclasses of
typeas potential metaclasses.