Class basics

This section will help get you started annotating your classes. Built-in classes such as int also follow these same rules.

Instance and class attributes

Mypy type checker detects if you are trying to access a missing attribute, which is a very common programming error. For this to work correctly, instance and class attributes must be defined or initialized within the class. Mypy infers the types of attributes:

class A:
    def __init__(self, x: int) -> None:
        self.x = x  # Aha, attribute 'x' of type 'int'

a = A(1)
a.x = 2  # OK!
a.y = 3  # Error: 'A' has no attribute 'y'

This is a bit like each class having an implicitly defined __slots__ attribute. This is only enforced during type checking and not when your program is running.

You can declare types of variables in the class body explicitly using a type annotation:

class A:
    x: List[int]  # Declare attribute 'x' of type List[int]

a = A()
a.x = [1]     # OK

As in Python generally, a variable defined in the class body can used as a class or an instance variable.

Type comments work as well, if you need to support Python versions earlier than 3.6:

class A:
    x = None  # type: List[int]  # Declare attribute 'x' of type List[int]

Note that attribute definitions in the class body that use a type comment are special: a None value is valid as the initializer, even though the declared type is not optional. This should be used sparingly, as this can result in None-related runtime errors that mypy can’t detect.

Similarly, you can give explicit types to instance variables defined in a method:

class A:
    def __init__(self) -> None:
        self.x: List[int] = []

    def f(self) -> None:
        self.y: Any = 0

You can only define an instance variable within a method if you assign to it explicitly using self:

class A:
    def __init__(self) -> None:
        self.y = 1   # Define 'y'
        a = self
        a.x = 1      # Error: 'x' not defined

Overriding statically typed methods

When overriding a statically typed method, mypy checks that the override has a compatible signature:

class A:
    def f(self, x: int) -> None:
        ...

class B(A):
    def f(self, x: str) -> None:   # Error: type of 'x' incompatible
        ...

class C(A):
    def f(self, x: int, y: int) -> None:  # Error: too many arguments
        ...

class D(A):
    def f(self, x: int) -> None:   # OK
        ...

Note

You can also vary return types covariantly in overriding. For example, you could override the return type object with a subtype such as int. Similarly, you can vary argument types contravariantly – subclasses can have more general argument types.

You can also override a statically typed method with a dynamically typed one. This allows dynamically typed code to override methods defined in library classes without worrying about their type signatures.

As always, relying on dynamically typed code can be unsafe. There is no runtime enforcement that the method override returns a value that is compatible with the original return type, since annotations have no effect at runtime:

class A:
    def inc(self, x: int) -> int:
        return x + 1

class B(A):
    def inc(self, x):   # Override, dynamically typed
        return 'hello'  # Incompatible with 'A', but no mypy error

Abstract base classes and multiple inheritance

Mypy supports Python abstract base classes (ABCs). Abstract classes have at least one abstract method or property that must be implemented by a subclass. You can define abstract base classes using the abc.ABCMeta metaclass, and the abc.abstractmethod and abc.abstractproperty function decorators. Example:

from abc import ABCMeta, abstractmethod

class A(metaclass=ABCMeta):
    @abstractmethod
    def foo(self, x: int) -> None: pass

    @abstractmethod
    def bar(self) -> str: pass

class B(A):
    def foo(self, x: int) -> None: ...
    def bar(self) -> str:
        return 'x'

a = A()  # Error: 'A' is abstract
b = B()  # OK

Note that mypy performs checking for unimplemented abstract methods even if you omit the ABCMeta metaclass. This can be useful if the metaclass would cause runtime metaclass conflicts.

A class can inherit any number of classes, both abstract and concrete. As with normal overrides, a dynamically typed method can implement a statically typed method defined in any base class, including an abstract method defined in an abstract base class.

You can implement an abstract property using either a normal property or an instance variable.