typing — type hints for readable, tool-checkable code#

Part of a series demonstrating the 5 most-imported modules across the top PyPI packages (from imports_ranked.csv). Rank 1 of 5 — imported by 67.7% of crawled packages.

The most-imported module in the corpus. typing provides the vocabulary for annotating variables, functions, and classes. Hints are not enforced at runtime — they document intent and power tools like mypy, IDEs, and dataclasses.

Function & variable annotations#

Annotations attach types to parameters, return values, and variables.

from typing import List, Dict, Optional, Union, Tuple

def greet(name: str, times: int = 1) -> str:
    return (f'Hi {name}! ' * times).strip()

count: int = 3
scores: Dict[str, float] = {'ada': 9.5, 'linus': 8.0}
greet('Manas', count), scores
('Hi Manas! Hi Manas! Hi Manas!', {'ada': 9.5, 'linus': 8.0})

Optional, Union, and container generics#

Optional[X] is shorthand for Union[X, None]. Containers can be parameterised: List[int], Dict[str, float], Tuple[int, ...].

def find_user(uid: int) -> Optional[str]:
    table: Dict[int, str] = {1: 'root'}
    return table.get(uid)          # str or None

def parse(x: Union[int, str]) -> int:
    return int(x)

print(find_user(1), find_user(99))
print(parse('42'), parse(42))
root None
42 42

Introspecting hints: get_type_hints, get_origin, get_args#

from typing import get_type_hints, get_origin, get_args

print(get_type_hints(greet))
print(get_origin(Dict[str, float]), get_args(Dict[str, float]))
print(get_origin(Optional[int]), get_args(Optional[int]))
{'name': <class 'str'>, 'times': <class 'int'>, 'return': <class 'str'>}
<class 'dict'> (<class 'str'>, <class 'float'>)
typing.Union (<class 'int'>, <class 'NoneType'>)

TypeVar and Generic — reusable, parameterised types#

from typing import TypeVar, Generic

T = TypeVar('T')

class Box(Generic[T]):
    def __init__(self, item: T) -> None:
        self.item = item
    def get(self) -> T:
        return self.item

b: Box[int] = Box(10)
b.get()
10

NamedTuple and TypedDict — typed records#

from typing import NamedTuple, TypedDict

class Point(NamedTuple):
    x: int
    y: int = 0

class Movie(TypedDict):
    title: str
    year: int

p = Point(1, 2)
m: Movie = {'title': 'Contact', 'year': 1997}
print(p, p.x, p._asdict())
print(m['title'], m['year'])
Point(x=1, y=2) 1 {'x': 1, 'y': 2}
Contact 1997

Literal, Callable, and Any#

from typing import Literal, Callable, Any

Mode = Literal['r', 'w', 'a']

def apply(fn: Callable[[int, int], int], a: int, b: int) -> int:
    return fn(a, b)

def opener(path: str, mode: Mode = 'r') -> str:
    return f'{path} opened as {mode}'

anything: Any = 123
print(apply(lambda a, b: a + b, 3, 4))
print(opener('data.txt', 'w'))
print(anything)
7
data.txt opened as w
123

Protocol — structural (duck) typing#

A class satisfies a Protocol if it has the right methods, without explicitly inheriting from it.

from typing import Protocol

class Sized(Protocol):
    def __len__(self) -> int: ...

class Boy():
    pass
    
def total_len(items: list[Sized]) -> int:
    for i in items:
        print(len(i))
    return sum(len(i) for i in items)

total_len(['abc', [1, 2], {'k': 'v'}, Boy()])   # all have __len__
3
2
1
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[7], line 14
     10     for i in items:
     11         print(len(i))
     12     return sum(len(i) for i in items)
     13 
---> 14 total_len(['abc', [1, 2], {'k': 'v'}, Boy()])   # all have __len__

Cell In[7], line 11, in total_len(items)
      9 def total_len(items: list[Sized]) -> int:
     10     for i in items:
---> 11         print(len(i))
     12     return sum(len(i) for i in items)

TypeError: object of type 'Boy' has no len()

Runtime reality check#

Hints do not enforce types. Python runs happily with ‘wrong’ values; static checkers are what catch the mismatch.

def add(a: int, b: int) -> int:
    return a + b

add('foo', 'bar')   # no error at runtime -> 'foobar'
'foobar'
import typing
print(dir(typing))            # list of names (public, private, everything in it is displayed)
['ABCMeta', 'AbstractSet', 'Annotated', 'Any', 'AnyStr', 'AsyncContextManager', 'AsyncGenerator', 'AsyncIterable', 'AsyncIterator', 'Awaitable', 'BinaryIO', 'ByteString', 'CT_co', 'Callable', 'ChainMap', 'ClassVar', 'Collection', 'Concatenate', 'Container', 'ContextManager', 'Coroutine', 'Counter', 'DefaultDict', 'Deque', 'Dict', 'EXCLUDED_ATTRIBUTES', 'Final', 'ForwardRef', 'FrozenSet', 'Generator', 'Generic', 'GenericAlias', 'Hashable', 'IO', 'ItemsView', 'Iterable', 'Iterator', 'KT', 'KeysView', 'List', 'Literal', 'LiteralString', 'Mapping', 'MappingView', 'Match', 'MethodDescriptorType', 'MethodWrapperType', 'MutableMapping', 'MutableSequence', 'MutableSet', 'NamedTuple', 'NamedTupleMeta', 'Never', 'NewType', 'NoReturn', 'NotRequired', 'Optional', 'OrderedDict', 'ParamSpec', 'ParamSpecArgs', 'ParamSpecKwargs', 'Pattern', 'Protocol', 'Required', 'Reversible', 'Self', 'Sequence', 'Set', 'Sized', 'SupportsAbs', 'SupportsBytes', 'SupportsComplex', 'SupportsFloat', 'SupportsIndex', 'SupportsInt', 'SupportsRound', 'T', 'TYPE_CHECKING', 'T_co', 'T_contra', 'Text', 'TextIO', 'Tuple', 'Type', 'TypeAlias', 'TypeAliasType', 'TypeGuard', 'TypeVar', 'TypeVarTuple', 'TypedDict', 'Union', 'Unpack', 'VT', 'VT_co', 'V_co', 'ValuesView', 'WrapperDescriptorType', '_ASSERT_NEVER_REPR_MAX_LENGTH', '_AnnotatedAlias', '_AnyMeta', '_BaseGenericAlias', '_CallableGenericAlias', '_CallableType', '_ConcatenateGenericAlias', '_DeprecatedGenericAlias', '_DeprecatedType', '_Final', '_Func', '_GenericAlias', '_IdentityCallable', '_LiteralGenericAlias', '_LiteralSpecialForm', '_NamedTuple', '_NotIterable', '_PROTO_ALLOWLIST', '_PickleUsingNameMixin', '_ProtocolMeta', '_SPECIAL_NAMES', '_SpecialForm', '_SpecialGenericAlias', '_TYPING_INTERNALS', '_TupleType', '_TypedDict', '_TypedDictMeta', '_TypingEllipsis', '_UnionGenericAlias', '_UnpackGenericAlias', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_alias', '_allow_reckless_class_checks', '_allowed_types', '_caches', '_caller', '_check_generic', '_cleanups', '_collect_parameters', '_compare_args_orderless', '_deduplicate', '_deduplicate_unhashable', '_eval_type', '_flatten_literal_params', '_generic_class_getitem', '_generic_init_subclass', '_get_protocol_attrs', '_idfunc', '_is_dunder', '_is_param_expr', '_is_typevar_like', '_is_unpacked_typevartuple', '_lazy_load_getattr_static', '_make_nmtuple', '_make_union', '_namedtuple_mro_entries', '_no_init_or_replace_init', '_overload_dummy', '_overload_registry', '_paramspec_prepare_subst', '_paramspec_subst', '_prohibited', '_proto_hook', '_remove_dups_flatten', '_should_unflatten_callable_args', '_special', '_strip_annotations', '_tp_cache', '_type_check', '_type_convert', '_type_repr', '_typevar_subst', '_typevartuple_prepare_subst', '_unpack_args', '_value_and_type_iter', 'abstractmethod', 'assert_never', 'assert_type', 'cast', 'clear_overloads', 'collections', 'contextlib', 'copyreg', 'dataclass_transform', 'defaultdict', 'final', 'functools', 'get_args', 'get_origin', 'get_overloads', 'get_type_hints', 'io', 'is_typeddict', 'no_type_check', 'no_type_check_decorator', 'operator', 'overload', 'override', 're', 'reveal_type', 'runtime_checkable', 'stdlib_re', 'sys', 'types', 'warnings']
print(typing.__all__)         # the officially exported names
['Annotated', 'Any', 'Callable', 'ClassVar', 'Concatenate', 'Final', 'ForwardRef', 'Generic', 'Literal', 'Optional', 'ParamSpec', 'Protocol', 'Tuple', 'Type', 'TypeVar', 'TypeVarTuple', 'Union', 'AbstractSet', 'ByteString', 'Container', 'ContextManager', 'Hashable', 'ItemsView', 'Iterable', 'Iterator', 'KeysView', 'Mapping', 'MappingView', 'MutableMapping', 'MutableSequence', 'MutableSet', 'Sequence', 'Sized', 'ValuesView', 'Awaitable', 'AsyncIterator', 'AsyncIterable', 'Coroutine', 'Collection', 'AsyncGenerator', 'AsyncContextManager', 'Reversible', 'SupportsAbs', 'SupportsBytes', 'SupportsComplex', 'SupportsFloat', 'SupportsIndex', 'SupportsInt', 'SupportsRound', 'ChainMap', 'Counter', 'Deque', 'Dict', 'DefaultDict', 'List', 'OrderedDict', 'Set', 'FrozenSet', 'NamedTuple', 'TypedDict', 'Generator', 'BinaryIO', 'IO', 'Match', 'Pattern', 'TextIO', 'AnyStr', 'assert_type', 'assert_never', 'cast', 'clear_overloads', 'dataclass_transform', 'final', 'get_args', 'get_origin', 'get_overloads', 'get_type_hints', 'is_typeddict', 'LiteralString', 'Never', 'NewType', 'no_type_check', 'no_type_check_decorator', 'NoReturn', 'NotRequired', 'overload', 'override', 'ParamSpecArgs', 'ParamSpecKwargs', 'Required', 'reveal_type', 'runtime_checkable', 'Self', 'Text', 'TYPE_CHECKING', 'TypeAlias', 'TypeGuard', 'TypeAliasType', 'Unpack']
print(typing.__file__)
/opt/homebrew/Cellar/python@3.12/3.12.11/Frameworks/Python.framework/Versions/3.12/lib/python3.12/typing.py