1283 words
6 minutes
Python typing

I. Python Typing (类型注解)
Python's
typing module adds type hints (类型提示) to Python. While Python remains dynamically typed, type hints help with code documentation, IDE autocompletion, and catching bugs before runtime using tools like mypy.
1. Basic Type Annotations
1) Variable Annotations
# Basic typesname: str = "Alice"age: int = 30height: float = 1.75is_student: bool = True
# Without initial valueaddress: str # Just type annotation, no value yetaddress = "123 Main St" # Later assignment2) Function Annotations
def greet(name: str) -> str: return f"Hello, {name}!"
def add(a: int, b: int) -> int: return a + b
def process_data(data: list) -> None: # None means returns nothing print(f"Processing {len(data)} items")Note: Type hints are not enforced at runtime. They're for developers and tools only. Python won't stop you from passing an
int to a str parameter!2. Container Types
1) List, Tuple, Set, Dict
from typing import List, Tuple, Set, Dict
# List of stringsnames: List[str] = ["Alice", "Bob", "Charlie"]
# Tuple of int and str (fixed length, mixed types)person: Tuple[int, str, bool] = (1, "Alice", True)
# Set of integersunique_ids: Set[int] = {101, 102, 103}
# Dictionary with string keys and int valuesscores: Dict[str, int] = {"Alice": 95, "Bob": 87}2) Nested Containers
from typing import List, Dict, Tuple
# List of dictionariesusers: List[Dict[str, str]] = [ {"name": "Alice", "email": "alice@example.com"}, {"name": "Bob", "email": "bob@example.com"}]
# Complex nestingmatrix: List[List[int]] = [[1, 2, 3], [4, 5, 6]]
# Tuple with list insidedata: Tuple[int, List[str], bool] = (1, ["a", "b", "c"], True)3. Optional and Union Types
1) Optional (value or None)
from typing import Optional
def find_user(user_id: int) -> Optional[str]: # Returns name or None if not found users = {1: "Alice", 2: "Bob"} return users.get(user_id) # May return None
# Optional[str] means either str or Noneresult: Optional[str] = find_user(1) # "Alice"result2: Optional[str] = find_user(99) # None2) Union (multiple possible types)
from typing import Union
# Function accepts int OR floatdef square(value: Union[int, float]) -> Union[int, float]: return value * value
# Modern syntax (Python 3.10+)def square2(value: int | float) -> int | float: return value * value
# Multiple typesdef process_id(id_value: int | str) -> str: return f"ID: {id_value}"
Python 3.10+ introduced the
| syntax for Union types, making code cleaner!
4. Type Aliases
Create readable names for complex types:
from typing import List, Tuple, Dict
# Type aliasUserID = intUserName = strUserInfo = Tuple[UserID, UserName, bool]
# Using the aliasdef get_user() -> UserInfo: return (1, "Alice", True)
# More complex aliasCoordinate = Tuple[float, float]Polygon = List[Coordinate]
def calculate_area(shape: Polygon) -> float: # shape is list of (x, y) tuples pass5. Callable Types
Type functions that accept functions:
from typing import Callable
# Function that takes an int and returns a strdef apply_twice(func: Callable[[int], str], value: int) -> str: return func(func(value)) # First call returns str, second call fails!
# Fixed version - proper typing catches this!def apply_twice_fixed(func: Callable[[int], int], value: int) -> int: return func(func(value))
# More complex callbackdef process_items( items: List[int], callback: Callable[[int], str]) -> List[str]: return [callback(item) for item in items]6. Any and TypeVar (Generics)
1) Any - escape hatch
from typing import Any
# Use sparingly - defeats type checking!def debug_print(value: Any) -> None: print(f"Value: {value}, Type: {type(value)}")2) TypeVar - generic functions
from typing import TypeVar, List
T = TypeVar('T') # Generic type variable
def first_element(items: List[T]) -> T: """Returns first element, type preserved""" return items[0]
# Works with any list typenum = first_element([1, 2, 3]) # num is inttext = first_element(["a", "b", "c"]) # text is str
# Multiple type variablesK = TypeVar('K')V = TypeVar('V')
def get_value(dict: Dict[K, V], key: K, default: V) -> V: return dict.get(key, default)7. Special Forms
1) Literal - exact values
from typing import Literal
def set_status(status: Literal["active", "inactive", "pending"]) -> None: print(f"Status set to {status}")
set_status("active") # OKset_status("active") # OK# set_status("unknown") # Type checker would complain
# Multiple literal valuesdef move(direction: Literal["north", "south", "east", "west"]) -> None: pass2) Final - constants
from typing import Final
MAX_SIZE: Final[int] = 100PI: Final[float] = 3.14159
# Type checker would warn against reassignment# MAX_SIZE = 200 # Error!8. Protocol (Structural Subtyping)
Like interfaces, but duck-typed:
from typing import Protocol
class Drawable(Protocol): def draw(self) -> None: ...
class Circle: def draw(self) -> None: print("Drawing circle")
class Square: def draw(self) -> None: print("Drawing square") def area(self) -> float: return 16.0
def render(obj: Drawable) -> None: obj.draw() # Works with anything that has draw()
render(Circle()) # OKrender(Square()) # OK - Square has draw()9. TypedDict - Dictionary with fixed keys
from typing import TypedDict
class Person(TypedDict): name: str age: int email: str
# Works like a dict, but with type checkingalice: Person = { "name": "Alice", "age": 30, "email": "alice@example.com"}
# Error if missing keys or wrong types# bob: Person = {"name": "Bob"} # Missing age, email10. Practical Examples
1) API Response Handler
from typing import Dict, List, Optional, Union, TypedDictimport json
class User(TypedDict): id: int name: str email: str is_active: bool
class APIResponse(TypedDict): status: Literal["success", "error"] data: Optional[List[User]] message: Optional[str]
def parse_api_response(json_str: str) -> APIResponse: data = json.loads(json_str) return { "status": data["status"], "data": data.get("users"), "message": data.get("message") }2) Data Processing Pipeline
from typing import List, Callable, TypeVar
T = TypeVar('T')U = TypeVar('U')
def pipeline( data: List[T], *transforms: Callable[[T], U]) -> List[U]: """Apply multiple transforms to data""" result: List[U] = [] for item in data: current = item for transform in transforms: current = transform(current) # type: ignore result.append(current) # type: ignore return result
# Usagenumbers = [1, 2, 3, 4]result = pipeline( numbers, lambda x: x * 2, lambda x: str(x)) # result is List[str]3) Configuration Manager
from typing import Dict, Any, Optionalfrom dataclasses import dataclass
@dataclassclass DatabaseConfig: host: str port: int = 5432 username: str password: str database: str
@dataclassclass AppConfig: debug: bool = False database: DatabaseConfig allowed_hosts: List[str] secret_key: Optional[str] = None
def load_config(config_dict: Dict[str, Any]) -> AppConfig: db_config = DatabaseConfig(**config_dict["database"]) return AppConfig( debug=config_dict.get("debug", False), database=db_config, allowed_hosts=config_dict["allowed_hosts"], secret_key=config_dict.get("secret_key") )11. Type Checking Tools
🔧 Tools to check types:
-
mypy - Most popular
Terminal window pip install mypymypy your_script.py -
pydantic - Runtime type checking + data validation
from pydantic import BaseModelclass User(BaseModel):name: strage: intuser = User(name="Alice", age=30) # Validates at runtime! -
VS Code/PyCharm - Built-in type checking with Pylance/Pyright
12. Best Practices
✅ DO:
# 1. Use type hints for public APIsdef calculate_total(prices: List[float]) -> float: return sum(prices)
# 2. Use Optional for values that can be Nonedef find_by_id(id: int) -> Optional[User]: pass
# 3. Use type aliases for complex typesJSON = Dict[str, Any]
# 4. Use Literal for limited optionsdef set_mode(mode: Literal["read", "write", "append"]) -> None: pass❌ DON’T:
# 1. Don't overuse Any (defeats purpose)def process(data: Any) -> Any: # Better to be specific
# 2. Don't ignore type errors without reasonresult = func() # type: ignore # Add comment explaining why
# 3. Don't use type hints for everything in simple scripts# For small scripts, they can be overkill13. Comparison Table
| Feature | Python 3.8-3.9 | Python 3.10+ | Python 3.11+ |
|---|---|---|---|
| Union | Union[int, str] | int | str | int | str |
| Optional | Optional[str] | str | None | str | None |
| List type | List[int] | list[int] | list[int] |
| Dict type | Dict[str, int] | dict[str, int] | dict[str, int] |
| Self reference | Forward ref 'MyClass' | Forward ref 'MyClass' | Self type |
💡 One-line Takeaway
Python's
Python's
typing adds optional type hints (类型提示) that won't affect runtime but make code self-documenting, IDE-friendly, and catch bugs early with tools like mypy.