Basic

Functions

Validate the input and output types on a function.

def greeting(name: str, count: int) -> str:
    return f"Hello {name} - your count is {count}"
from typing import List

def greet_all(names: List[str]) -> str:
    return f"Hello, {','.join(names)}"

Create a new reusable type called Vector, to make your code more expressive.

from typing import List

Vector = List[float]

def scale(scalar: float, vector: Vector) -> Vector:
    return [scalar * num for num in vector]

new_vector = scale(2.0, [1.0, -4.2, 5.4])

If you leave out the types on the function, they just won’t be checked.

Variables

You can specify a type on a variable, but this is optional. Also you can get the type checking benefit without adding it yourself, if the type can be inferred.

# With type. This doesn't add much.
x: int = 1
# Without type specified. This will be inferred as int.
y = 2

# Initialize with type but no value. This will be the null value e.g. `0`.
z: int

# Example of initializing a null value.
child: bool
if age < 18:
    child = True
else:
    child = False

Typing

Using the Typing module.

Based on Type hints cheat sheet (Python 3).

Note from Python 3.9, you can use dict instead of Dict as so on, avoiding imports.

Built-in types

x: int = 1
x: float = 1.0
x: bool = True
x: str = "test"
x: bytes = b"test"
x: List[int] = [1]
x: Set[int] = {6, 7}

x = [1]  # type: List[int]

A dictionary.

x: Dict[str, float] = {
    'field': 2.0
}

See also the Typed dictionary section.

Fixed size tuple.

x: Tuple[int, str, float] = (3, "yes", 7.5)

For a variable size tuple - use ellipsis.

x: Tuple[int, ...] = (1, 2, 3)

Allow a value to be None.

x: Optional[str] = some_function()

Mypy understands a value can’t be None in used an if-statement.

if x is not None:
    print(x.upper())

If a value can never be None due to some invariants, use an assert.

assert x is not None
print(x.upper())

Tuple

Tuple[TYPE, TYPE, ...]

e.g.

from typing import Tuple


def foo() -> Tuple[bool, str]:
    return True, 'Yes'

Union

Allow a variable to be one of given types.

Union[TYPE, TYPE, ...]

Here we allow an integer or string.

def bar(bazz: bool) -> Union[int, str]:
    if bazz:
        return 'Yes'

    return 12

Optional

Allow a variable to bw None.

Optional[TYPE]

Declare a type for a variable. This was recommended by the docs.

foo: Optional[str] = None

foo = 'abc'

Here we return a string or None. The second case shows a necessary annotation. Note you must declare the time on the first declariation from top to bottom - not the 2nd and not both.

def foo() -> Optional[str]:
    pass


def bar(bazz: bool) -> Optional[str]:
    if bazz:
        buzz: Optional[str] = 'Yes'
    else:
        buzz = None
    
    return buzz

You may can an error from Pylint:

E1136: Value 'Optional' is unsubscriptable (unsubscriptable-object)

You can use use Union instead. But then you have to use it twice.

def bar(bazz: bool) -> Union[str, None]:
    if bazz:
        buzz: Union[str, None] = 'Yes'
    else:
        buzz = None
    return buzz

Or you have to use Optional like this.

def bar(bazz: bool) -> Union[str, None]:
    if bazz:
        buzz: Optional[str] = 'Yes'
    else:
        buzz = None
    return buzz

Unless you use a return sooner and so don’t define buzz.

Typed dictionary

Define and used a TypeDict type. This is resuable, unlike a plain Dict[TYPE, TYPE] setup.

Example from TypedDict section of Mypy docs.

from typing_extensions import TypedDict

Define a type.

Movie = TypedDict('Movie', {'name': str, 'year': int})

Use the type. NB. You must add the comment explicitly.

movie = {'name': 'Blade Runner', 'year': 1982}  # type: Movie

Use it as a constructor.

toy_story = Movie(name='Toy Story', year=1995)

Which is the equivalent of a plain dict with a type annotation.

toy_story =  dict(name='Toy Story', year=1995)  # type: Movie

When you use a typed dictionary, Mypy can detect an invalid key or value type and given an error:

director = movie['director']  # Error: 'director' is not a valid key