from typing importList, Set, Dict, Tuple, Optional # For simple built-in types, just use the name of the type x: int = 1 x: float = 1.0 x: bool = True x: str = "test" x: bytes = b"test"
# For collections, the type of the collection item is in brackets # (Python 3.9+) x: list[int] = [1] x: set[int] = {6, 7}
# In Python 3.8 and earlier, the name of the collection type is # capitalized, and the type is imported from the 'typing' module x: List[int] = [1] x: Set[int] = {6, 7}
# For mappings, we need the types of both keys and values x: dict[str, float] = {"field": 2.0} # Python 3.9+ x: Dict[str, float] = {"field": 2.0}
# For tuples of fixed size, we specify the types of all the elements x: tuple[int, str, float] = (3, "yes", 7.5) # Python 3.9+ x: Tuple[int, str, float] = (3, "yes", 7.5)
# For tuples of variable size, we use one type and ellipsis x: tuple[int, ...] = (1, 2, 3) # Python 3.9+ x: Tuple[int, ...] = (1, 2, 3)
# Use Optional[] for values that could be None x: Optional[str] = some_function() # Mypy understands a value can't be None in an if-statement if x isnotNone: print(x.upper()) # If a value can never be None due to some invariants, use an assert assert x isnotNone print(x.upper())
from typing importCallable, Iterator, Union, Optional
# This is how you annotate a function definition defstringify(num: int) -> str: returnstr(num)
# And here's how you specify multiple arguments defplus(num1: int, num2: int) -> int: return num1 + num2
# Add default value for an argument after the type annotation deff(num1: int, my_float: float = 3.5) -> float: return num1 + my_float
# This is how you annotate a callable (function) value x: Callable[[int, float], float] = f
# A generator function that yields ints is secretly just a function that # returns an iterator of ints, so that's how we annotate it defg(n: int) -> Iterator[int]: i = 0 while i < n: yield i i += 1
# You can of course split a function annotation over multiple lines defsend_email(address: Union[str, list[str]], sender: str, cc: Optional[list[str]], bcc: Optional[list[str]], subject='', body: Optional[list[str]] = None ) -> bool: ...
# An argument can be declared positional-only by giving it a name # starting with two underscores: defquux(__x: int) -> None: pass
# To find out what type mypy infers for an expression anywhere in # your program, wrap it in reveal_type(). Mypy will print an error # message with the type; remove it again before running the code. reveal_type(1) # -> Revealed type is "builtins.int"
# Use Union when something could be one of a few types x: list[Union[int, str]] = [3, 5, "test", "fun"]
# Use Any if you don't know the type of something or it's too # dynamic to write a type for x: Any = mystery_function()
# If you initialize a variable with an empty container or "None" # you may have to help mypy a bit by providing a type annotation x: list[str] = [] x: Optional[str] = None
# This makes each positional arg and each keyword arg a "str" defcall(self, *args: str, **kwargs: str) -> str: request = make_request(*args, **kwargs) return self.do_api_query(request)
# Use a "type: ignore" comment to suppress errors on a given line, # when your code confuses mypy or runs into an outright bug in mypy. # Good practice is to comment every "ignore" with a bug link # (in mypy, typeshed, or your own code) or an explanation of the issue. x = confusing_function() # type: ignore # https://github.com/python/mypy/issues/1167
# "cast" is a helper function that lets you override the inferred # type of an expression. It's only for mypy -- there's no runtime check. a = [4] b = cast(list[int], a) # Passes fine c = cast(list[str], a) # Passes fine (no runtime check) reveal_type(c) # -> Revealed type is "builtins.list[builtins.str]" print(c) # -> [4]; the object is not cast
# If you want dynamic attributes on your class, have it override "__setattr__" # or "__getattr__" in a stub or in your source code. # # "__setattr__" allows for dynamic assignment to names # "__getattr__" allows for dynamic access to names classA: # This will allow assignment to any A.x, if x is the same type as "value" # (use "value: Any" to allow arbitrary types) def__setattr__(self, name: str, value: int) -> None: ...
# This will allow access to any A.x, if x is compatible with the return type def__getattr__(self, name: str) -> int: ...
a.foo = 42# Works a.bar = 'Ex-parrot'# Fails type checking
from typing import Mapping, MutableMapping, Sequence, Iterable
# Use Iterable for generic iterables (anything usable in "for"), # and Sequence where a sequence (supporting "len" and "__getitem__") is # required deff(ints: Iterable[int]) -> list[str]: return [str(x) for x in ints]
f(range(1, 3))
# Mapping describes a dict-like object (with "__getitem__") that we won't # mutate, and MutableMapping one (with "__setitem__") that we might deff(my_mapping: Mapping[int, str]) -> list[int]: my_mapping[5] = 'maybe'# if we try this, mypy will throw an error... returnlist(my_mapping.keys())
f({3: 'yes', 4: 'no'})
deff(my_mapping: MutableMapping[int, str]) -> set[str]: my_mapping[5] = 'maybe'# ...but mypy is OK with this. returnset(my_mapping.values())
classMyClass: # You can optionally declare instance variables in the class body attr: int # This is an instance variable with a default value charge_percent: int = 100
# The "__init__" method doesn't return anything, so it gets return # type "None" just like any other method that doesn't return anything def__init__(self) -> None: ...
# For instance methods, omit type for "self" defmy_method(self, num: int, str1: str) -> str: return num * str1
# User-defined classes are valid as types in annotations x: MyClass = MyClass()
# You can use the ClassVar annotation to declare a class variable classCar: seats: ClassVar[int] = 4 passengers: ClassVar[list[str]]
# You can also declare the type of an attribute in "__init__" classBox: def__init__(self) -> None: self.items: list[str] = []
# "typing.Match" describes regex matches from the re module x: Match[str] = re.match(r'[0-9]+', "15")
# Use IO[] for functions that should accept or return any # object that comes from an open() call (IO[] does not # distinguish between reading, writing or other modes) defget_sys_IO(mode: str = 'w') -> IO[str]: if mode == 'w': return sys.stdout elif mode == 'r': return sys.stdin else: return sys.stdout
# Forward references are useful if you want to reference a class before # it is defined deff(foo: A) -> int: # This will fail ...
classA: ...
# If you use the string literal 'A', it will pass as long as there is a # class of that name later on in the file deff(foo: 'A') -> int: # Ok ...