Python Typing

Python Typing

Tags
python
Published
December 1, 2023
Author
Chris Chan
 
My summary of https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html. One of the most widely used type checkers in use for Python is mypy. Just call mypy <filename> to run the type checking.

Built-in Types

 
# Primitives x: int = 1 x: float = 1.0 x: bool = True x: str = "test" x: bytes = b"test" # Collections x: list[int] = [] x: list[list[int]] = [[]] x: set[int] = {6, 7} # Mappings x: dict[str, float] = {"field": 2.0} # Tuples of fixed size x: tuple[int, str, float] = (3, "yes", 7.5) # Tuples with variable size x: tuple[int, ...] = (1, 2, 3) # When something is one of few types x: list[int | str] = [3, 5, "test", "fun"] # Use Optional[X] for something that could be none. x: Optional[str] = "something" if some_condition else None
 

Functions

from typing import Callable, Iterator, Union, Optional # Specify arguments and return value. def add(num1: int, num2: int) -> int: return num1 + num2 # Use None for no return value. def print_log(debug: str) -> None print(debug) # Arguments without type are treated as Any def untyped(x): pass # Annotate Callable x: Callable[[int, float], float] = f def register(callback: Callable[[str], int]) -> None: pass # Annotate Generate def gen(n: int) -> Iterator[int]: i = 0 while i < n: yield i i += 1 # Multiline annotation def send_email(address: Union[str, list[str]], sender: str, cc: Optional[list[str]], bcc: Optional[list[str]], subject: str = '', body: Optional[list[str]] = None ) -> bool: pass
 

Classes

# Constructor returns None # Instance methods omit type for "self" class BankAccount: def __init__(self, account_name: str, initial_balance: int = 0) -> None: self.account_name = account_name self.initial_balance = initial_balance def deposit(self, amount: int) -> None: self.balance += amount def withdraw(self, amount: int) -> None: self.balance -= amount # Using user defined class. account: BankAccount = BankAccount("bob", 300) def transfer(src: BankAccount, dst: BankAccount, amount: int) -> None: src.withdraw(amount) dst.deposit(amount) # Functions that accept base class can also accept subclass. def AuditedBankAccount(BankAccount): def __init__(self, account_name: str, initial_balance: int = 0) -> None: super().__init__(account_name, initial_balance) self.audit_log: list[str] = [] def deposit(self, amount:int) -> None: self.audit_log.append(f"Deposited {amount}") self.balance += amount def withdraw(self, amount:int) -> None: self.audit_log.append(f"Withdrew {amount}") self.balance -= amount audited = AuditedBankAccount("alice", 300) transfer(audited, account, 100) # Declare class variable with ClassVar class Car: seats: ClassVar[int] = 4 passengers: ClassVar[list[str]] # Override "__setattr__" or "__getattr__" for dynamic attributes. class SomeClass: # 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 = SomeClass() a.foo = 42 # Works a.bar = 'Ex-parrot' # Fails type checking
 
 

Puzzling Situations

# use "# type: ignore" to ignore x = confusing_function() # type: ignore # Use typing.cast to override inferred type a = [4] b = cast(list[int], a) # Passes fine