# For most types, just use the name of the type in the annotation# Note that mypy can usually infer the type of a variable from its value,# so technically these annotations are redundantx: int = 1x: float = 1.0x: bool = Truex: str = "test"x: bytes = b"test"# For collections on Python 3.9+, the type of the collection item is in bracketsx: list[int] = [1]x: set[int] = {6, 7}# For mappings, we need the types of both keys and valuesx: dict[str, float] = {"field": 2.0} # Python 3.9+# For tuples of fixed size, we specify the types of all the elementsx: tuple[int, str, float] = (3, "yes", 7.5) # Python 3.9+# For tuples of variable size, we use one type and ellipsisx: tuple[int, ...] = (1, 2, 3) # Python 3.9+# On Python 3.8 and earlier, the name of the collection type is# capitalized, and the type is imported from the 'typing' modulefrom typing import List, Set, Dict, Tuplex: List[int] = [1]x: Set[int] = {6, 7}x: Dict[str, float] = {"field": 2.0}x: Tuple[int, str, float] = (3, "yes", 7.5)x: Tuple[int, ...] = (1, 2, 3)from typing import Union, Optional# On Python 3.10+, use the | operator when something could be one of a few typesx: list[int | str] = [3, 5, "test", "fun"] # Python 3.10+# On earlier versions, use Unionx: list[Union[int, str]] = [3, 5, "test", "fun"]# Use X | None for a value that could be None on Python 3.10+# Use Optional[X] on 3.9 and earlier; Optional[X] is the same as 'X | None'x: str | None = "something" if some_condition() else Noneif x is not None: # Mypy understands x won't be None here because of the if-statement print(x.upper())# If you know a value can never be None due to some logic that mypy doesn't# understand, use an assertassert x is not Noneprint(x.upper())
from collections.abc import Iterator, Callablefrom typing import Union, Optional# This is how you annotate a function definitiondef stringify(num: int) -> str: return str(num)# And here's how you specify multiple argumentsdef plus(num1: int, num2: int) -> int: return num1 + num2# If a function does not return a value, use None as the return type# Default value for an argument goes after the type annotationdef show(value: str, excitement: int = 10) -> None: print(value + "!" * excitement)# Note that arguments without a type are dynamically typed (treated as Any)# and that functions without any annotations are not checkeddef untyped(x): x.anything() + 1 + "string" # no errors# This is how you annotate a callable (function) valuex: Callable[[int, float], float] = fdef register(callback: Callable[[str], int]) -> None: ...# A generator function that yields ints is secretly just a function that# returns an iterator of ints, so that's how we annotate itdef gen(n: int) -> Iterator[int]: i = 0 while i < n: yield i i += 1# You can of course split a function annotation over multiple linesdef send_email( address: str | list[str], sender: str, cc: list[str] | None, bcc: list[str] | None, subject: str = '', body: list[str] | None = None,) -> bool: ...# Mypy understands positional-only and keyword-only arguments# Positional-only arguments can also be marked by using a name starting with# two underscoresdef quux(x: int, /, *, y: int) -> None: passquux(3, y=5) # Okquux(3, 5) # error: Too many positional arguments for "quux"quux(x=3, y=5) # error: Unexpected keyword argument "x" for "quux"# This says each positional arg and each keyword arg is a "str"def call(self, *args: str, **kwargs: str) -> str: reveal_type(args) # Revealed type is "tuple[str, ...]" reveal_type(kwargs) # Revealed type is "dict[str, str]" request = make_request(*args, **kwargs) return self.do_api_query(request)