# Metaclasses

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 `Enum`s, `NamedTuple`s and singletons.

Mypy has some special understanding of `ABCMeta` and `EnumMeta`.

## Defining a metaclass

    class M(type):
        pass

    class A(metaclass=M):
        pass

In Python 2, the syntax for defining a metaclass is different:

    class A(object):
        __metaclass__ = M

Mypy also supports using `six.with_metaclass()` and `@six.add_metaclass` to define metaclass in a portable way:

    import six

    class A(six.with_metaclass(M)):
        pass

    @six.add_metaclass(M)
    class C(object):
        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: Inconsistent metaclass structure for 'B1'
    # 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

    # Same runtime error as in B1, but mypy does not catch it yet
    class B12(A1, A2): pass

- 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 `type` as potential metaclasses.