mypy
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinesemypy - Static Type Checking for Python
mypy - Python静态类型检查
Overview
概述
mypy is the standard static type checker for Python, enabling gradual typing with type hints (PEP 484) and comprehensive type safety. It catches type errors before runtime, improves code documentation, and enhances IDE support while maintaining Python's dynamic nature through incremental adoption.
Key Features:
- Gradual typing: Add types incrementally to existing code
- Strict mode: Maximum type safety with --strict flag
- Type inference: Automatically infer types from context
- Protocol support: Structural typing (duck typing with types)
- Generic types: TypeVar, Generic, and advanced type patterns
- Framework integration: FastAPI, Django, Pydantic compatibility
- Plugin system: Extend type checking for libraries
- Incremental checking: Fast type checking on large codebases
Installation:
bash
undefinedmypy是Python的标准静态类型检查工具,通过类型提示(PEP 484)实现渐进式类型检查,提供全面的类型安全保障。它能在运行前捕获类型错误,改进代码文档,增强IDE支持,同时通过增量式采用保留Python的动态特性。
核心特性:
- 渐进式类型:为现有代码逐步添加类型提示
- 严格模式:通过--strict标志实现最高级别类型安全
- 类型推断:从上下文自动推断类型
- Protocol支持:结构化类型(带类型的鸭子类型)
- 泛型类型:TypeVar、Generic及高级类型模式
- 框架集成:兼容FastAPI、Django、Pydantic
- 插件系统:扩展库的类型检查能力
- 增量检查:对大型代码库快速进行类型检查
安装:
bash
undefinedBasic mypy
Basic mypy
pip install mypy
pip install mypy
With common type stubs
With common type stubs
pip install mypy types-requests types-PyYAML types-redis
pip install mypy types-requests types-PyYAML types-redis
For FastAPI projects
For FastAPI projects
pip install mypy pydantic
pip install mypy pydantic
For Django projects
For Django projects
pip install mypy django-stubs
pip install mypy django-stubs
Development setup
Development setup
pip install mypy pre-commit
undefinedpip install mypy pre-commit
undefinedType Annotation Basics
类型注解基础
1. Variable Type Hints
1. 变量类型提示
python
undefinedpython
undefinedBasic types
Basic types
name: str = "Alice"
age: int = 30
height: float = 5.9
is_active: bool = True
name: str = "Alice"
age: int = 30
height: float = 5.9
is_active: bool = True
Type inference (mypy infers types)
Type inference (mypy infers types)
count = 10 # mypy infers: int
message = "Hello" # mypy infers: str
count = 10 # mypy infers: int
message = "Hello" # mypy infers: str
Multiple types with Union
Multiple types with Union
from typing import Union
user_id: Union[int, str] = 123 # Can be int OR str
result: Union[int, None] = None # Nullable int
from typing import Union
user_id: Union[int, str] = 123 # Can be int OR str
result: Union[int, None] = None # Nullable int
Optional (shorthand for Union[T, None])
Optional (shorthand for Union[T, None])
from typing import Optional
user_email: Optional[str] = None # Can be str or None
undefinedfrom typing import Optional
user_email: Optional[str] = None # Can be str or None
undefined2. Function Type Hints
2. 函数类型提示
python
undefinedpython
undefinedBasic function typing
Basic function typing
def greet(name: str) -> str:
return f"Hello, {name}"
def greet(name: str) -> str:
return f"Hello, {name}"
Multiple parameters
Multiple parameters
def add(a: int, b: int) -> int:
return a + b
def add(a: int, b: int) -> int:
return a + b
Optional parameters with defaults
Optional parameters with defaults
def create_user(name: str, age: int = 18) -> dict:
return {"name": name, "age": age}
def create_user(name: str, age: int = 18) -> dict:
return {"name": name, "age": age}
No return value
No return value
def log_message(message: str) -> None:
print(message)
def log_message(message: str) -> None:
print(message)
Functions that never return
Functions that never return
from typing import NoReturn
def raise_error() -> NoReturn:
raise ValueError("Always raises")
undefinedfrom typing import NoReturn
def raise_error() -> NoReturn:
raise ValueError("Always raises")
undefined3. Collection Type Hints
3. 集合类型提示
python
from typing import List, Dict, Set, Tuplepython
from typing import List, Dict, Set, TupleList with element type
List with element type
numbers: List[int] = [1, 2, 3, 4]
names: List[str] = ["Alice", "Bob", "Charlie"]
numbers: List[int] = [1, 2, 3, 4]
names: List[str] = ["Alice", "Bob", "Charlie"]
Dict with key and value types
Dict with key and value types
user_ages: Dict[str, int] = {"Alice": 30, "Bob": 25}
config: Dict[str, Union[str, int]] = {"host": "localhost", "port": 8000}
user_ages: Dict[str, int] = {"Alice": 30, "Bob": 25}
config: Dict[str, Union[str, int]] = {"host": "localhost", "port": 8000}
Set with element type
Set with element type
unique_ids: Set[int] = {1, 2, 3}
unique_ids: Set[int] = {1, 2, 3}
Tuple with fixed types
Tuple with fixed types
coordinate: Tuple[float, float] = (10.5, 20.3)
user_record: Tuple[int, str, bool] = (1, "Alice", True)
coordinate: Tuple[float, float] = (10.5, 20.3)
user_record: Tuple[int, str, bool] = (1, "Alice", True)
Variable-length tuple
Variable-length tuple
numbers: Tuple[int, ...] = (1, 2, 3, 4, 5)
numbers: Tuple[int, ...] = (1, 2, 3, 4, 5)
Modern syntax (Python 3.9+)
Modern syntax (Python 3.9+)
numbers: list[int] = [1, 2, 3]
user_ages: dict[str, int] = {"Alice": 30}
undefinednumbers: list[int] = [1, 2, 3]
user_ages: dict[str, int] = {"Alice": 30}
undefined4. Class Type Hints
4. 类类型提示
python
class User:
# Class attributes
name: str
age: int
email: Optional[str]
def __init__(self, name: str, age: int, email: Optional[str] = None) -> None:
self.name = name
self.age = age
self.email = email
def get_info(self) -> Dict[str, Union[str, int]]:
return {
"name": self.name,
"age": self.age,
"email": self.email or "N/A"
}
@classmethod
def from_dict(cls, data: Dict[str, any]) -> "User":
return cls(
name=data["name"],
age=data["age"],
email=data.get("email")
)python
class User:
# Class attributes
name: str
age: int
email: Optional[str]
def __init__(self, name: str, age: int, email: Optional[str] = None) -> None:
self.name = name
self.age = age
self.email = email
def get_info(self) -> Dict[str, Union[str, int]]:
return {
"name": self.name,
"age": self.age,
"email": self.email or "N/A"
}
@classmethod
def from_dict(cls, data: Dict[str, any]) -> "User":
return cls(
name=data["name"],
age=data["age"],
email=data.get("email")
)Advanced Type Hints
高级类型提示
1. Literal Types
1. 字面量类型
python
from typing import Literalpython
from typing import LiteralRestrict to specific values
Restrict to specific values
def set_log_level(level: Literal["debug", "info", "warning", "error"]) -> None:
print(f"Log level: {level}")
def set_log_level(level: Literal["debug", "info", "warning", "error"]) -> None:
print(f"Log level: {level}")
Valid
Valid
set_log_level("debug")
set_log_level("error")
set_log_level("debug")
set_log_level("error")
Type error: Argument 1 has incompatible type "verbose"
Type error: Argument 1 has incompatible type "verbose"
set_log_level("verbose")
set_log_level("verbose")
Multiple literals
Multiple literals
Status = Literal["pending", "approved", "rejected"]
def update_status(status: Status) -> None:
pass
undefinedStatus = Literal["pending", "approved", "rejected"]
def update_status(status: Status) -> None:
pass
undefined2. Type Aliases
2. 类型别名
python
from typing import Dict, List, Unionpython
from typing import Dict, List, UnionSimple alias
Simple alias
UserId = int
UserName = str
def get_user(user_id: UserId) -> UserName:
return f"User {user_id}"
UserId = int
UserName = str
def get_user(user_id: UserId) -> UserName:
return f"User {user_id}"
Complex aliases
Complex aliases
JSON = Union[Dict[str, "JSON"], List["JSON"], str, int, float, bool, None]
Headers = Dict[str, str]
QueryParams = Dict[str, Union[str, int, List[str]]]
def make_request(
url: str,
headers: Headers,
params: QueryParams
) -> JSON:
pass
JSON = Union[Dict[str, "JSON"], List["JSON"], str, int, float, bool, None]
Headers = Dict[str, str]
QueryParams = Dict[str, Union[str, int, List[str]]]
def make_request(
url: str,
headers: Headers,
params: QueryParams
) -> JSON:
pass
NewType for distinct types
NewType for distinct types
from typing import NewType
UserId = NewType("UserId", int)
ProductId = NewType("ProductId", int)
def get_user(user_id: UserId) -> str:
return f"User {user_id}"
user = UserId(123) # Valid
product = ProductId(456)
get_user(user) # Valid
get_user(product) # Type error: ProductId not compatible with UserId
undefinedfrom typing import NewType
UserId = NewType("UserId", int)
ProductId = NewType("ProductId", int)
def get_user(user_id: UserId) -> str:
return f"User {user_id}"
user = UserId(123) # Valid
product = ProductId(456)
get_user(user) # Valid
get_user(product) # Type error: ProductId not compatible with UserId
undefined3. Generics and TypeVar
3. 泛型与TypeVar
python
from typing import TypeVar, Generic, Listpython
from typing import TypeVar, Generic, ListTypeVar for generic functions
TypeVar for generic functions
T = TypeVar("T")
def first_element(items: List[T]) -> T:
return items[0]
T = TypeVar("T")
def first_element(items: List[T]) -> T:
return items[0]
Type inference
Type inference
num = first_element([1, 2, 3]) # mypy infers: int
name = first_element(["Alice", "Bob"]) # mypy infers: str
num = first_element([1, 2, 3]) # mypy infers: int
name = first_element(["Alice", "Bob"]) # mypy infers: str
Bounded TypeVar
Bounded TypeVar
from typing import Union
NumericType = TypeVar("NumericType", int, float)
def add_numbers(a: NumericType, b: NumericType) -> NumericType:
return a + b
from typing import Union
NumericType = TypeVar("NumericType", int, float)
def add_numbers(a: NumericType, b: NumericType) -> NumericType:
return a + b
Generic classes
Generic classes
class Stack(Generic[T]):
def init(self) -> None:
self._items: List[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
def is_empty(self) -> bool:
return len(self._items) == 0class Stack(Generic[T]):
def init(self) -> None:
self._items: List[T] = []
def push(self, item: T) -> None:
self._items.append(item)
def pop(self) -> T:
return self._items.pop()
def is_empty(self) -> bool:
return len(self._items) == 0Usage with type inference
Usage with type inference
int_stack: Stack[int] = Stack()
int_stack.push(42)
int_stack.push("hello") # Type error: Expected int, got str
str_stack: Stack[str] = Stack()
str_stack.push("hello") # Valid
undefinedint_stack: Stack[int] = Stack()
int_stack.push(42)
int_stack.push("hello") # Type error: Expected int, got str
str_stack: Stack[str] = Stack()
str_stack.push("hello") # Valid
undefined4. Protocol (Structural Typing)
4. Protocol(结构化类型)
python
from typing import Protocolpython
from typing import ProtocolDefine protocol (interface)
Define protocol (interface)
class Drawable(Protocol):
def draw(self) -> str:
...
class Drawable(Protocol):
def draw(self) -> str:
...
Any class with draw() method matches
Any class with draw() method matches
class Circle:
def draw(self) -> str:
return "Drawing circle"
class Square:
def draw(self) -> str:
return "Drawing square"
class Circle:
def draw(self) -> str:
return "Drawing circle"
class Square:
def draw(self) -> str:
return "Drawing square"
Function accepts any Drawable
Function accepts any Drawable
def render(obj: Drawable) -> str:
return obj.draw()
def render(obj: Drawable) -> str:
return obj.draw()
Both work (duck typing with types)
Both work (duck typing with types)
circle = Circle()
square = Square()
render(circle) # Valid
render(square) # Valid
circle = Circle()
square = Square()
render(circle) # Valid
render(square) # Valid
Runtime checkable protocols
Runtime checkable protocols
from typing import runtime_checkable
@runtime_checkable
class Closeable(Protocol):
def close(self) -> None:
...
class File:
def close(self) -> None:
pass
from typing import runtime_checkable
@runtime_checkable
class Closeable(Protocol):
def close(self) -> None:
...
class File:
def close(self) -> None:
pass
Runtime check
Runtime check
f = File()
isinstance(f, Closeable) # True
undefinedf = File()
isinstance(f, Closeable) # True
undefined5. Callable Types
5. 可调用类型
python
from typing import Callablepython
from typing import CallableFunction that takes another function
Function that takes another function
def apply_twice(func: Callable[[int], int], value: int) -> int:
return func(func(value))
def double(x: int) -> int:
return x * 2
result = apply_twice(double, 5) # Returns 20
def apply_twice(func: Callable[[int], int], value: int) -> int:
return func(func(value))
def double(x: int) -> int:
return x * 2
result = apply_twice(double, 5) # Returns 20
Generic callable
Generic callable
from typing import TypeVar
T = TypeVar("T")
R = TypeVar("R")
def map_values(
func: Callable[[T], R],
values: List[T]
) -> List[R]:
return [func(v) for v in values]
from typing import TypeVar
T = TypeVar("T")
R = TypeVar("R")
def map_values(
func: Callable[[T], R],
values: List[T]
) -> List[R]:
return [func(v) for v in values]
Callable with multiple arguments
Callable with multiple arguments
Validator = Callable[[str, int], bool]
def validate_user(name: str, age: int) -> bool:
return len(name) > 0 and age >= 0
validator: Validator = validate_user
undefinedValidator = Callable[[str, int], bool]
def validate_user(name: str, age: int) -> bool:
return len(name) > 0 and age >= 0
validator: Validator = validate_user
undefinedmypy Configuration
mypy配置
1. mypy.ini Configuration
1. mypy.ini配置
ini
undefinedini
undefinedmypy.ini
mypy.ini
[mypy]
[mypy]
Python version
Python version
python_version = 3.11
python_version = 3.11
Import discovery
Import discovery
files = src,tests
exclude = build,dist,venv
files = src,tests
exclude = build,dist,venv
Type checking strictness
Type checking strictness
disallow_untyped_defs = True
disallow_any_unimported = False
no_implicit_optional = True
warn_return_any = True
warn_unused_ignores = True
warn_redundant_casts = True
disallow_untyped_defs = True
disallow_any_unimported = False
no_implicit_optional = True
warn_return_any = True
warn_unused_ignores = True
warn_redundant_casts = True
Error reporting
Error reporting
show_error_codes = True
show_column_numbers = True
pretty = True
show_error_codes = True
show_column_numbers = True
pretty = True
Incremental type checking
Incremental type checking
incremental = True
cache_dir = .mypy_cache
incremental = True
cache_dir = .mypy_cache
Per-module configuration
Per-module configuration
[mypy-tests.*]
disallow_untyped_defs = False
[mypy-migrations.*]
ignore_errors = True
[mypy-tests.*]
disallow_untyped_defs = False
[mypy-migrations.*]
ignore_errors = True
Third-party libraries without stubs
Third-party libraries without stubs
[mypy-redis.*]
ignore_missing_imports = True
[mypy-celery.*]
ignore_missing_imports = True
undefined[mypy-redis.*]
ignore_missing_imports = True
[mypy-celery.*]
ignore_missing_imports = True
undefined2. pyproject.toml Configuration
2. pyproject.toml配置
toml
undefinedtoml
undefinedpyproject.toml
pyproject.toml
[tool.mypy]
python_version = "3.11"
files = ["src", "tests"]
exclude = ["build", "dist", "venv"]
[tool.mypy]
python_version = "3.11"
files = ["src", "tests"]
exclude = ["build", "dist", "venv"]
Strictness
Strictness
disallow_untyped_defs = true
disallow_any_unimported = false
no_implicit_optional = true
warn_return_any = true
warn_unused_ignores = true
warn_redundant_casts = true
strict_equality = true
strict_concatenate = true
disallow_untyped_defs = true
disallow_any_unimported = false
no_implicit_optional = true
warn_return_any = true
warn_unused_ignores = true
warn_redundant_casts = true
strict_equality = true
strict_concatenate = true
Error reporting
Error reporting
show_error_codes = true
show_column_numbers = true
pretty = true
color_output = true
show_error_codes = true
show_column_numbers = true
pretty = true
color_output = true
Incremental
Incremental
incremental = true
cache_dir = ".mypy_cache"
incremental = true
cache_dir = ".mypy_cache"
Per-module overrides
Per-module overrides
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
[[tool.mypy.overrides]]
module = ["redis.", "celery."]
ignore_missing_imports = true
undefined[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
[[tool.mypy.overrides]]
module = ["redis.", "celery."]
ignore_missing_imports = true
undefined3. Strict Mode
3. 严格模式
bash
undefinedbash
undefinedEnable all strict checks
Enable all strict checks
mypy --strict src/
mypy --strict src/
Strict mode equivalent flags
Strict mode equivalent flags
mypy
--disallow-any-unimported
--disallow-any-expr
--disallow-any-decorated
--disallow-any-explicit
--disallow-any-generics
--disallow-subclassing-any
--disallow-untyped-calls
--disallow-untyped-defs
--disallow-incomplete-defs
--check-untyped-defs
--disallow-untyped-decorators
--no-implicit-optional
--warn-redundant-casts
--warn-unused-ignores
--warn-return-any
--warn-unreachable
--strict-equality
src/
--disallow-any-unimported
--disallow-any-expr
--disallow-any-decorated
--disallow-any-explicit
--disallow-any-generics
--disallow-subclassing-any
--disallow-untyped-calls
--disallow-untyped-defs
--disallow-incomplete-defs
--check-untyped-defs
--disallow-untyped-decorators
--no-implicit-optional
--warn-redundant-casts
--warn-unused-ignores
--warn-return-any
--warn-unreachable
--strict-equality
src/
```inimypy
--disallow-any-unimported
--disallow-any-expr
--disallow-any-decorated
--disallow-any-explicit
--disallow-any-generics
--disallow-subclassing-any
--disallow-untyped-calls
--disallow-untyped-defs
--disallow-incomplete-defs
--check-untyped-defs
--disallow-untyped-decorators
--no-implicit-optional
--warn-redundant-casts
--warn-unused-ignores
--warn-return-any
--warn-unreachable
--strict-equality
src/
--disallow-any-unimported
--disallow-any-expr
--disallow-any-decorated
--disallow-any-explicit
--disallow-any-generics
--disallow-subclassing-any
--disallow-untyped-calls
--disallow-untyped-defs
--disallow-incomplete-defs
--check-untyped-defs
--disallow-untyped-decorators
--no-implicit-optional
--warn-redundant-casts
--warn-unused-ignores
--warn-return-any
--warn-unreachable
--strict-equality
src/
```inimypy.ini strict configuration
mypy.ini strict configuration
[mypy]
strict = True
[mypy]
strict = True
Relax specific checks if needed
Relax specific checks if needed
disallow_any_expr = False # Too strict for most projects
disallow_any_explicit = False # Allow explicit Any
undefineddisallow_any_expr = False # Too strict for most projects
disallow_any_explicit = False # Allow explicit Any
undefinedIncremental Adoption Strategies
增量式采用策略
1. Start with Entry Points
1. 从入口文件开始
python
undefinedpython
undefinedStart typing from main.py (top-level)
Start typing from main.py (top-level)
main.py
main.py
from typing import Optional
from app.services import UserService
def main(config_path: Optional[str] = None) -> None:
"""Application entry point."""
service = UserService()
service.run()
if name == "main":
main()
```bashfrom typing import Optional
from app.services import UserService
def main(config_path: Optional[str] = None) -> None:
"""Application entry point."""
service = UserService()
service.run()
if name == "main":
main()
```bashCheck only main.py initially
Check only main.py initially
mypy main.py
mypy main.py
Gradually expand scope
Gradually expand scope
mypy main.py app/services.py
mypy src/
undefinedmypy main.py app/services.py
mypy src/
undefined2. Per-Module Strict Mode
2. 按模块启用严格模式
ini
undefinedini
undefinedmypy.ini - Gradually enable strict checking
mypy.ini - Gradually enable strict checking
[mypy]
[mypy]
Lenient global defaults
Lenient global defaults
ignore_missing_imports = True
disallow_untyped_defs = False
ignore_missing_imports = True
disallow_untyped_defs = False
Strict for new modules
Strict for new modules
[mypy-app.services.user_service]
disallow_untyped_defs = True
warn_return_any = True
[mypy-app.api.*]
disallow_untyped_defs = True
no_implicit_optional = True
[mypy-app.services.user_service]
disallow_untyped_defs = True
warn_return_any = True
[mypy-app.api.*]
disallow_untyped_defs = True
no_implicit_optional = True
Still lenient for legacy code
Still lenient for legacy code
[mypy-app.legacy.*]
ignore_errors = True
undefined[mypy-app.legacy.*]
ignore_errors = True
undefined3. Use # type: ignore Strategically
3. 策略性使用# type: ignore
python
undefinedpython
undefinedSuppress specific errors during migration
Suppress specific errors during migration
import legacy_module # type: ignore[import]
def process_data(data): # type: ignore[no-untyped-def]
# TODO: Add type hints
return data.transform()
import legacy_module # type: ignore[import]
def process_data(data): # type: ignore[no-untyped-def]
# TODO: Add type hints
return data.transform()
Ignore specific error codes
Ignore specific error codes
user_dict = get_user_dict()
user_id = user_dict["id"] # type: ignore[index]
user_dict = get_user_dict()
user_id = user_dict["id"] # type: ignore[index]
Ignore entire line (use sparingly)
Ignore entire line (use sparingly)
result = external_api.call() # type: ignore
undefinedresult = external_api.call() # type: ignore
undefined4. Reveal Types During Development
4. 开发期间使用reveal_type
python
from typing import reveal_type
def process_user(user_id: int):
user = get_user(user_id)
reveal_type(user) # mypy will show inferred type
name = user.name
reveal_type(name) # mypy will show: strpython
from typing import reveal_type
def process_user(user_id: int):
user = get_user(user_id)
reveal_type(user) # mypy will show inferred type
name = user.name
reveal_type(name) # mypy will show: strFastAPI Integration
FastAPI集成
1. FastAPI with Type Hints
1. 带类型提示的FastAPI
python
undefinedpython
undefinedmain.py
main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
app = FastAPI()
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List, Optional
app = FastAPI()
Pydantic models (auto-validated)
Pydantic models (auto-validated)
class User(BaseModel):
id: int
name: str
email: str
age: Optional[int] = None
class UserCreate(BaseModel):
name: str
email: str
age: Optional[int] = None
class User(BaseModel):
id: int
name: str
email: str
age: Optional[int] = None
class UserCreate(BaseModel):
name: str
email: str
age: Optional[int] = None
Type-safe endpoints
Type-safe endpoints
@app.get("/")
def read_root() -> dict[str, str]:
return {"message": "Hello World"}
@app.get("/users/{user_id}")
def read_user(user_id: int) -> User:
if user_id == 0:
raise HTTPException(status_code=404, detail="User not found")
return User(id=user_id, name=f"User {user_id}", email="user@example.com")
@app.get("/users")
def list_users(skip: int = 0, limit: int = 10) -> List[User]:
users = [
User(id=i, name=f"User {i}", email=f"user{i}@example.com")
for i in range(skip, skip + limit)
]
return users
@app.post("/users")
def create_user(user: UserCreate) -> User:
# Pydantic ensures type safety
return User(id=1, name=user.name, email=user.email, age=user.age)
undefined@app.get("/")
def read_root() -> dict[str, str]:
return {"message": "Hello World"}
@app.get("/users/{user_id}")
def read_user(user_id: int) -> User:
if user_id == 0:
raise HTTPException(status_code=404, detail="User not found")
return User(id=user_id, name=f"User {user_id}", email="user@example.com")
@app.get("/users")
def list_users(skip: int = 0, limit: int = 10) -> List[User]:
users = [
User(id=i, name=f"User {i}", email=f"user{i}@example.com")
for i in range(skip, skip + limit)
]
return users
@app.post("/users")
def create_user(user: UserCreate) -> User:
# Pydantic ensures type safety
return User(id=1, name=user.name, email=user.email, age=user.age)
undefined2. Async FastAPI Type Checking
2. 异步FastAPI类型检查
python
from typing import List, Optional
from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import AsyncSession
app = FastAPI()python
from typing import List, Optional
from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import AsyncSession
app = FastAPI()Async dependency
Async dependency
async def get_db() -> AsyncSession:
async with AsyncSessionLocal() as session:
yield session
async def get_db() -> AsyncSession:
async with AsyncSessionLocal() as session:
yield session
Async endpoints with types
Async endpoints with types
@app.get("/users/{user_id}")
async def read_user(
user_id: int,
db: AsyncSession = Depends(get_db)
) -> User:
user = await db.get(User, user_id)
if user is None:
raise HTTPException(status_code=404, detail="User not found")
return user
@app.get("/users")
async def list_users(
skip: int = 0,
limit: int = 10,
db: AsyncSession = Depends(get_db)
) -> List[User]:
result = await db.execute(
select(User).offset(skip).limit(limit)
)
return result.scalars().all()
undefined@app.get("/users/{user_id}")
async def read_user(
user_id: int,
db: AsyncSession = Depends(get_db)
) -> User:
user = await db.get(User, user_id)
if user is None:
raise HTTPException(status_code=404, detail="User not found")
return user
@app.get("/users")
async def list_users(
skip: int = 0,
limit: int = 10,
db: AsyncSession = Depends(get_db)
) -> List[User]:
result = await db.execute(
select(User).offset(skip).limit(limit)
)
return result.scalars().all()
undefined3. FastAPI Dependency Injection Types
3. FastAPI依赖注入类型
python
from typing import Annotated, Optional
from fastapi import FastAPI, Depends, Header, HTTPException
app = FastAPI()python
from typing import Annotated, Optional
from fastapi import FastAPI, Depends, Header, HTTPException
app = FastAPI()Typed dependencies
Typed dependencies
async def get_current_user(
authorization: Annotated[Optional[str], Header()] = None
) -> User:
if authorization is None:
raise HTTPException(status_code=401, detail="Not authenticated")
# Verify token and return user
return User(id=1, name="Current User", email="user@example.com")
async def get_current_user(
authorization: Annotated[Optional[str], Header()] = None
) -> User:
if authorization is None:
raise HTTPException(status_code=401, detail="Not authenticated")
# Verify token and return user
return User(id=1, name="Current User", email="user@example.com")
Use dependency with type annotation
Use dependency with type annotation
@app.get("/me")
async def read_current_user(
current_user: Annotated[User, Depends(get_current_user)]
) -> User:
return current_user
@app.get("/me")
async def read_current_user(
current_user: Annotated[User, Depends(get_current_user)]
) -> User:
return current_user
Complex dependency chain
Complex dependency chain
class UserService:
def init(self, db: AsyncSession) -> None:
self.db = db
async def get_user(self, user_id: int) -> Optional[User]:
return await self.db.get(User, user_id)def get_user_service(
db: Annotated[AsyncSession, Depends(get_db)]
) -> UserService:
return UserService(db)
@app.get("/users/{user_id}")
async def get_user_endpoint(
user_id: int,
service: Annotated[UserService, Depends(get_user_service)]
) -> User:
user = await service.get_user(user_id)
if user is None:
raise HTTPException(status_code=404, detail="User not found")
return user
undefinedclass UserService:
def init(self, db: AsyncSession) -> None:
self.db = db
async def get_user(self, user_id: int) -> Optional[User]:
return await self.db.get(User, user_id)def get_user_service(
db: Annotated[AsyncSession, Depends(get_db)]
) -> UserService:
return UserService(db)
@app.get("/users/{user_id}")
async def get_user_endpoint(
user_id: int,
service: Annotated[UserService, Depends(get_user_service)]
) -> User:
user = await service.get_user(user_id)
if user is None:
raise HTTPException(status_code=404, detail="User not found")
return user
undefinedDjango Integration
Django集成
1. Django with django-stubs
1. Django与django-stubs
bash
undefinedbash
undefinedInstall django-stubs
Install django-stubs
pip install django-stubs mypy
pip install django-stubs mypy
Generate mypy configuration
Generate mypy configuration
python -m mypy --install-types
```inipython -m mypy --install-types
```inimypy.ini
mypy.ini
[mypy]
plugins = mypy_django_plugin.main
[mypy.plugins.django-stubs]
django_settings_module = "myproject.settings"
undefined[mypy]
plugins = mypy_django_plugin.main
[mypy.plugins.django-stubs]
django_settings_module = "myproject.settings"
undefined2. Django Models with Type Hints
2. 带类型提示的Django模型
python
undefinedpython
undefinedmodels.py
models.py
from django.db import models
from typing import Optional
class User(models.Model):
email: models.EmailField = models.EmailField(unique=True)
name: models.CharField = models.CharField(max_length=100)
age: models.IntegerField = models.IntegerField(null=True, blank=True)
is_active: models.BooleanField = models.BooleanField(default=True)
created_at: models.DateTimeField = models.DateTimeField(auto_now_add=True)
def get_display_name(self) -> str:
return self.name or self.email
@classmethod
def get_active_users(cls) -> models.QuerySet["User"]:
return cls.objects.filter(is_active=True)undefinedfrom django.db import models
from typing import Optional
class User(models.Model):
email: models.EmailField = models.EmailField(unique=True)
name: models.CharField = models.CharField(max_length=100)
age: models.IntegerField = models.IntegerField(null=True, blank=True)
is_active: models.BooleanField = models.BooleanField(default=True)
created_at: models.DateTimeField = models.DateTimeField(auto_now_add=True)
def get_display_name(self) -> str:
return self.name or self.email
@classmethod
def get_active_users(cls) -> models.QuerySet["User"]:
return cls.objects.filter(is_active=True)undefined3. Django Views with Type Hints
3. 带类型提示的Django视图
python
undefinedpython
undefinedviews.py
views.py
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404
from typing import Dict, Any
from .models import User
def user_detail(request: HttpRequest, user_id: int) -> JsonResponse:
user: User = get_object_or_404(User, pk=user_id)
data: Dict[str, Any] = {
"id": user.id,
"name": user.name,
"email": user.email,
}
return JsonResponse(data)
def user_list(request: HttpRequest) -> JsonResponse:
users = User.get_active_users()
data = {
"users": list(users.values("id", "name", "email"))
}
return JsonResponse(data)
undefinedfrom django.http import HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import get_object_or_404
from typing import Dict, Any
from .models import User
def user_detail(request: HttpRequest, user_id: int) -> JsonResponse:
user: User = get_object_or_404(User, pk=user_id)
data: Dict[str, Any] = {
"id": user.id,
"name": user.name,
"email": user.email,
}
return JsonResponse(data)
def user_list(request: HttpRequest) -> JsonResponse:
users = User.get_active_users()
data = {
"users": list(users.values("id", "name", "email"))
}
return JsonResponse(data)
undefinedType Stubs and Third-Party Libraries
类型存根与第三方库
1. Installing Type Stubs
1. 安装类型存根
bash
undefinedbash
undefinedInstall stubs for popular libraries
Install stubs for popular libraries
pip install types-requests
pip install types-PyYAML
pip install types-redis
pip install types-boto3
pip install types-requests
pip install types-PyYAML
pip install types-redis
pip install types-boto3
Search for available stubs
Search for available stubs
pip search types-
pip search types-
Auto-install missing stubs
Auto-install missing stubs
mypy --install-types
undefinedmypy --install-types
undefined2. Creating Custom Stubs
2. 创建自定义类型存根
python
undefinedpython
undefinedstubs/external_lib.pyi
stubs/external_lib.pyi
from typing import Optional, List
class Client:
def init(self, api_key: str) -> None: ...
def get_user(self, user_id: int) -> Optional[dict]: ...
def list_users(self, limit: int = 10) -> List[dict]: ...def connect(host: str, port: int) -> Client: ...
```inifrom typing import Optional, List
class Client:
def init(self, api_key: str) -> None: ...
def get_user(self, user_id: int) -> Optional[dict]: ...
def list_users(self, limit: int = 10) -> List[dict]: ...def connect(host: str, port: int) -> Client: ...
```inimypy.ini
mypy.ini
[mypy]
mypy_path = stubs
undefined[mypy]
mypy_path = stubs
undefined3. Ignoring Missing Imports
3. 忽略缺失的导入
ini
undefinedini
undefinedmypy.ini
mypy.ini
[mypy-external_lib.*]
ignore_missing_imports = True
[mypy-external_lib.*]
ignore_missing_imports = True
For multiple libraries
For multiple libraries
[mypy-celery.,redis.,boto3.*]
ignore_missing_imports = True
undefined[mypy-celery.,redis.,boto3.*]
ignore_missing_imports = True
undefinedCI/CD Integration
CI/CD集成
1. GitHub Actions
1. GitHub Actions
yaml
undefinedyaml
undefined.github/workflows/type-check.yml
.github/workflows/type-check.yml
name: Type Check
on: [push, pull_request]
jobs:
mypy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install mypy
pip install -r requirements.txt
pip install types-requests types-PyYAML
- name: Run mypy
run: mypy src/
- name: Run mypy strict on new code
run: mypy --strict src/api/undefinedname: Type Check
on: [push, pull_request]
jobs:
mypy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install mypy
pip install -r requirements.txt
pip install types-requests types-PyYAML
- name: Run mypy
run: mypy src/
- name: Run mypy strict on new code
run: mypy --strict src/api/undefined2. Pre-commit Hook
2. Pre-commit钩子
yaml
undefinedyaml
undefined.pre-commit-config.yaml
.pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
args: [--strict, --ignore-missing-imports]
additional_dependencies:
- types-requests
- types-PyYAML
- id: mypy
args: [--strict, --ignore-missing-imports]
additional_dependencies:
```bashrepos:
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
args: [--strict, --ignore-missing-imports]
additional_dependencies:
- types-requests
- types-PyYAML
- id: mypy
args: [--strict, --ignore-missing-imports]
additional_dependencies:
```bashInstall pre-commit
Install pre-commit
pip install pre-commit
pre-commit install
pip install pre-commit
pre-commit install
Run manually
Run manually
pre-commit run mypy --all-files
undefinedpre-commit run mypy --all-files
undefined3. Make Target
3. Make目标
makefile
undefinedmakefile
undefinedMakefile
Makefile
.PHONY: typecheck
typecheck:
mypy src/
.PHONY: typecheck-strict
typecheck-strict:
mypy --strict src/
.PHONY: typecheck-report
typecheck-report:
mypy src/ --html-report mypy-report
@echo "Report: mypy-report/index.html"
.PHONY: ci
ci: typecheck test lint
undefined.PHONY: typecheck
typecheck:
mypy src/
.PHONY: typecheck-strict
typecheck-strict:
mypy --strict src/
.PHONY: typecheck-report
typecheck-report:
mypy src/ --html-report mypy-report
@echo "Report: mypy-report/index.html"
.PHONY: ci
ci: typecheck test lint
undefinedCommon Patterns and Idioms
常见模式与惯用写法
1. Overload for Multiple Signatures
1. 为多个签名使用Overload
python
from typing import overload, Union
@overload
def process(data: str) -> str: ...
@overload
def process(data: int) -> int: ...
@overload
def process(data: list) -> list: ...
def process(data: Union[str, int, list]) -> Union[str, int, list]:
"""Process different data types."""
if isinstance(data, str):
return data.upper()
elif isinstance(data, int):
return data * 2
else:
return [x * 2 for x in data]python
from typing import overload, Union
@overload
def process(data: str) -> str: ...
@overload
def process(data: int) -> int: ...
@overload
def process(data: list) -> list: ...
def process(data: Union[str, int, list]) -> Union[str, int, list]:
"""Process different data types."""
if isinstance(data, str):
return data.upper()
elif isinstance(data, int):
return data * 2
else:
return [x * 2 for x in data]mypy knows return types
mypy knows return types
result1: str = process("hello") # Valid
result2: int = process(42) # Valid
result3: str = process(42) # Type error
undefinedresult1: str = process("hello") # Valid
result2: int = process(42) # Valid
result3: str = process(42) # Type error
undefined2. TypedDict for Structured Dicts
2. 为结构化字典使用TypedDict
python
from typing import TypedDict, Optional
class UserDict(TypedDict):
id: int
name: str
email: str
age: Optional[int]python
from typing import TypedDict, Optional
class UserDict(TypedDict):
id: int
name: str
email: str
age: Optional[int]Type-safe dict usage
Type-safe dict usage
def create_user(data: UserDict) -> UserDict:
return {
"id": 1,
"name": data["name"],
"email": data["email"],
"age": data.get("age"),
}
user: UserDict = {
"id": 1,
"name": "Alice",
"email": "alice@example.com",
"age": 30
}
def create_user(data: UserDict) -> UserDict:
return {
"id": 1,
"name": data["name"],
"email": data["email"],
"age": data.get("age"),
}
user: UserDict = {
"id": 1,
"name": "Alice",
"email": "alice@example.com",
"age": 30
}
Type error: Missing required key "email"
Type error: Missing required key "email"
invalid_user: UserDict = {
"id": 1,
"name": "Bob",
}
undefinedinvalid_user: UserDict = {
"id": 1,
"name": "Bob",
}
undefined3. Final and Constant Values
3. Final与常量值
python
from typing import Finalpython
from typing import FinalConstants that should never change
Constants that should never change
API_VERSION: Final = "v1"
MAX_RETRIES: Final[int] = 3
API_VERSION: Final = "v1"
MAX_RETRIES: Final[int] = 3
Type error: Cannot assign to final name
Type error: Cannot assign to final name
API_VERSION = "v2"
API_VERSION = "v2"
Final class (cannot be subclassed)
Final class (cannot be subclassed)
from typing import final
@final
class BaseConfig:
pass
from typing import final
@final
class BaseConfig:
pass
Type error: Cannot inherit from final class
Type error: Cannot inherit from final class
class AppConfig(BaseConfig): # Error!
pass
undefinedclass AppConfig(BaseConfig): # Error!
pass
undefined4. Self Type for Method Chaining
4. 方法链式调用的Self类型
python
from typing import Self # Python 3.11+
class Builder:
def __init__(self) -> None:
self._value = 0
def add(self, value: int) -> Self:
self._value += value
return self
def multiply(self, value: int) -> Self:
self._value *= value
return self
def build(self) -> int:
return self._valuepython
from typing import Self # Python 3.11+
class Builder:
def __init__(self) -> None:
self._value = 0
def add(self, value: int) -> Self:
self._value += value
return self
def multiply(self, value: int) -> Self:
self._value *= value
return self
def build(self) -> int:
return self._valueType-safe method chaining
Type-safe method chaining
result = Builder().add(5).multiply(2).add(3).build()
undefinedresult = Builder().add(5).multiply(2).add(3).build()
undefinedmypy vs pyright Comparison
mypy与pyright对比
Feature Comparison
特性对比
| Feature | mypy | pyright |
|---|---|---|
| Type Checker | Official Python type checker | Microsoft's type checker |
| Speed | Slower on large codebases | Faster, incremental |
| Strictness | Configurable strict mode | Very strict by default |
| IDE Integration | Good (LSP support) | Excellent (Pylance in VS Code) |
| Plugin System | Yes (mypy plugins) | Limited |
| Error Messages | Clear, detailed | Very detailed, helpful |
| Community | Large, official | Growing, Microsoft-backed |
| Type Inference | Good | Excellent |
| Configuration | mypy.ini, pyproject.toml | pyrightconfig.json, pyproject.toml |
| 特性 | mypy | pyright |
|---|---|---|
| 类型检查器 | 官方Python类型检查器 | Microsoft开发的类型检查器 |
| 速度 | 大型代码库上速度较慢 | 更快,支持增量检查 |
| 严格性 | 可配置严格模式 | 默认非常严格 |
| IDE集成 | 良好(支持LSP) | 优秀(VS Code中的Pylance) |
| 插件系统 | 支持(mypy插件) | 有限 |
| 错误信息 | 清晰、详细 | 非常详细、有帮助 |
| 社区 | 庞大、官方支持 | 正在增长,由Microsoft支持 |
| 类型推断 | 良好 | 优秀 |
| 配置 | mypy.ini、pyproject.toml | pyrightconfig.json、pyproject.toml |
When to Use mypy
何时使用mypy
bash
undefinedbash
undefinedUse mypy for:
Use mypy for:
- Official Python type checking standard
- 官方Python类型检查标准
- Plugin ecosystem (Django, SQLAlchemy, Pydantic)
- 插件生态系统(Django、SQLAlchemy、Pydantic)
- Gradual typing with fine-grained control
- 细粒度控制的渐进式类型检查
- Compatibility with existing mypy configurations
- 与现有mypy配置兼容
- CI/CD pipelines (industry standard)
- CI/CD流水线(行业标准)
undefinedundefinedWhen to Use pyright
何时使用pyright
bash
undefinedbash
undefinedUse pyright for:
Use pyright for:
- VS Code development (Pylance)
- VS Code开发(Pylance)
- Faster type checking on large codebases
- 大型代码库上更快的类型检查
- Stricter type checking by default
- 默认更严格的类型检查
- Better type inference
- 更优秀的类型推断
- Real-time IDE feedback
- 实时IDE反馈
undefinedundefinedUsing Both
同时使用两者
toml
undefinedtoml
undefinedpyproject.toml - Configure both
pyproject.toml - Configure both
[tool.mypy]
strict = true
files = ["src"]
[tool.pyright]
include = ["src"]
strict = ["src/api"]
reportMissingTypeStubs = false
```bash[tool.mypy]
strict = true
files = ["src"]
[tool.pyright]
include = ["src"]
strict = ["src/api"]
reportMissingTypeStubs = false
```bashRun both in CI
Run both in CI
mypy src/
pyright src/
undefinedmypy src/
pyright src/
undefinedLocal mypy Profiles (Your Repos)
本地mypy配置文件(你的仓库)
Common patterns from your Python projects:
- Strict default (edgar, kuzu-memory, mcp-browser):
,
disallow_untyped_defs = true,check_untyped_defs = true,no_implicit_optional = true,warn_return_any = true.strict_equality = true - Relaxed profile (mcp-ticketer): strict flags disabled temporarily with a list for patch releases.
disable_error_code - Incremental adoption (mcp-vector-search): while stabilizing types.
ignore_errors = true - Missing imports: used in mcp-memory and mcp-ticketer.
ignore_missing_imports = true
Reference: see in , , , and .
pyproject.tomledgarkuzu-memorymcp-vector-searchmcp-ticketer你的Python项目中的常见模式:
- 严格默认配置(edgar、kuzu-memory、mcp-browser):
,
disallow_untyped_defs = true,check_untyped_defs = true,no_implicit_optional = true,warn_return_any = true。strict_equality = true - 宽松配置(mcp-ticketer):临时禁用严格标志,为补丁版本添加列表。
disable_error_code - 增量式采用(mcp-vector-search):稳定类型期间设置。
ignore_errors = true - 缺失导入处理:mcp-memory和mcp-ticketer中使用。
ignore_missing_imports = true
参考:查看、、和中的。
edgarkuzu-memorymcp-vector-searchmcp-ticketerpyproject.tomlBest Practices
最佳实践
1. Start with Key Modules
1. 从核心模块开始
python
undefinedpython
undefined✅ GOOD: Type critical business logic first
✅ 推荐:优先为关键业务逻辑添加类型
services/user_service.py
services/user_service.py
from typing import Optional
class UserService:
def get_user(self, user_id: int) -> Optional[User]:
"""Retrieve user by ID."""
return self.db.query(User).get(user_id)
def create_user(self, data: UserCreate) -> User:
"""Create new user."""
user = User(**data.dict())
self.db.add(user)
self.db.commit()
return userundefinedfrom typing import Optional
class UserService:
def get_user(self, user_id: int) -> Optional[User]:
"""根据ID检索用户。"""
return self.db.query(User).get(user_id)
def create_user(self, data: UserCreate) -> User:
"""创建新用户。"""
user = User(**data.dict())
self.db.add(user)
self.db.commit()
return userundefined2. Use Type Aliases for Readability
2. 使用类型别名提升可读性
python
undefinedpython
undefined✅ GOOD: Clear, reusable type aliases
✅ 推荐:清晰、可复用的类型别名
from typing import Dict, List, Union
JSON = Union[Dict[str, "JSON"], List["JSON"], str, int, float, bool, None]
Headers = Dict[str, str]
UserId = int
def parse_response(data: JSON, headers: Headers) -> UserId:
pass
from typing import Dict, List, Union
JSON = Union[Dict[str, "JSON"], List["JSON"], str, int, float, bool, None]
Headers = Dict[str, str]
UserId = int
def parse_response(data: JSON, headers: Headers) -> UserId:
pass
❌ BAD: Complex inline types
❌ 不推荐:复杂的内联类型
def parse_response(
data: Union[Dict[str, Union[...]], List[...], str, int, float, bool, None],
headers: Dict[str, str]
) -> int:
pass
undefineddef parse_response(
data: Union[Dict[str, Union[...]], List[...], str, int, float, bool, None],
headers: Dict[str, str]
) -> int:
pass
undefined3. Prefer Explicit Over Implicit
3. 优先显式类型而非隐式推断
python
undefinedpython
undefined✅ GOOD: Explicit types for public APIs
✅ 推荐:公共API使用显式类型
def get_user(user_id: int) -> Optional[User]:
return db.query(User).get(user_id)
def get_user(user_id: int) -> Optional[User]:
return db.query(User).get(user_id)
❌ ACCEPTABLE: Type inference for internal helpers
❌ 可接受:内部辅助函数使用类型推断
def _format_name(first, last): # mypy infers str -> str
return f"{first} {last}"
undefineddef _format_name(first, last): # mypy infers str -> str
return f"{first} {last}"
undefined4. Use reveal_type for Debugging
4. 使用reveal_type调试
python
undefinedpython
undefinedDuring development, check inferred types
开发期间,检查推断的类型
from typing import reveal_type
def process_data(data):
result = transform(data)
reveal_type(result) # mypy: Revealed type is "int"
return result * 2
undefinedfrom typing import reveal_type
def process_data(data):
result = transform(data)
reveal_type(result) # mypy: Revealed type is "int"
return result * 2
undefined5. Document Type Ignores
5. 为类型忽略添加文档说明
python
undefinedpython
undefined✅ GOOD: Document why type checking is disabled
✅ 推荐:说明禁用类型检查的原因
import legacy_module # type: ignore[import] # TODO: Add type stubs
import legacy_module # type: ignore[import] # TODO: 添加类型存根
❌ BAD: No explanation
❌ 不推荐:无任何说明
import legacy_module # type: ignore
undefinedimport legacy_module # type: ignore
undefinedCommon Pitfalls
常见陷阱
❌ Anti-Pattern 1: Using Any Everywhere
❌ 反模式1:到处使用Any
python
undefinedpython
undefinedWRONG: Defeats purpose of type checking
错误:失去类型检查的意义
from typing import Any
def process(data: Any) -> Any:
return data.transform()
**Correct:**
```python
from typing import Union, Protocol
class Transformable(Protocol):
def transform(self) -> dict: ...
def process(data: Transformable) -> dict:
return data.transform()from typing import Any
def process(data: Any) -> Any:
return data.transform()
**正确写法:**
```python
from typing import Union, Protocol
class Transformable(Protocol):
def transform(self) -> dict: ...
def process(data: Transformable) -> dict:
return data.transform()❌ Anti-Pattern 2: Ignoring Type Errors Globally
❌ 反模式2:全局忽略类型错误
ini
undefinedini
undefinedWRONG: Disables type checking
错误:完全禁用类型检查
[mypy]
ignore_errors = True
**Correct:**
```ini[mypy]
ignore_errors = True
**正确写法:**
```iniIgnore specific modules only
仅忽略特定模块
[mypy-legacy.*]
ignore_errors = True
[mypy]
strict = True
undefined[mypy-legacy.*]
ignore_errors = True
[mypy]
strict = True
undefined❌ Anti-Pattern 3: Not Using Optional
❌ 反模式3:不使用Optional
python
undefinedpython
undefinedWRONG: Nullable without Optional
错误:可空值未标记Optional
def get_user(user_id: int) -> User:
user = db.get(user_id) # Can be None!
return user # Runtime error if None
**Correct:**
```python
from typing import Optional
def get_user(user_id: int) -> Optional[User]:
return db.get(user_id)def get_user(user_id: int) -> User:
user = db.get(user_id) # 可能为None!
return user # 为None时会触发运行时错误
**正确写法:**
```python
from typing import Optional
def get_user(user_id: int) -> Optional[User]:
return db.get(user_id)Handle None explicitly
显式处理None情况
user = get_user(123)
if user is not None:
print(user.name)
undefineduser = get_user(123)
if user is not None:
print(user.name)
undefinedQuick Reference
快速参考
Common Commands
常用命令
bash
undefinedbash
undefinedBasic type checking
基础类型检查
mypy main.py
mypy src/
mypy main.py
mypy src/
Strict mode
严格模式
mypy --strict src/
mypy --strict src/
Install missing type stubs
安装缺失的类型存根
mypy --install-types
mypy --install-types
Generate HTML report
生成HTML报告
mypy src/ --html-report mypy-report
mypy src/ --html-report mypy-report
Check specific error codes
显示错误代码
mypy --show-error-codes src/
mypy --show-error-codes src/
Ignore missing imports
忽略缺失的导入
mypy --ignore-missing-imports src/
mypy --ignore-missing-imports src/
Follow imports
跟随导入
mypy --follow-imports=silent src/
mypy --follow-imports=silent src/
Incremental mode (faster)
增量模式(更快)
mypy --incremental src/
mypy --incremental src/
Verbose output
详细输出
mypy --verbose src/
undefinedmypy --verbose src/
undefinedError Code Reference
错误代码参考
bash
undefinedbash
undefinedCommon error codes
常见错误代码
[attr-defined] # Attribute not defined
[arg-type] # Argument type mismatch
[return-value] # Return type mismatch
[assignment] # Assignment type mismatch
[call-overload] # No matching overload
[index] # Invalid index operation
[operator] # Unsupported operand type
[import] # Cannot find import
[misc] # Miscellaneous type error
[no-untyped-def] # Function missing type annotation
[var-annotated] # Variable needs type annotation
undefined[attr-defined] # 属性未定义
[arg-type] # 参数类型不匹配
[return-value] # 返回类型不匹配
[assignment] # 赋值类型不匹配
[call-overload] # 无匹配的重载
[index] # 无效的索引操作
[operator] # 不支持的操作数类型
[import] # 无法找到导入
[misc] # 其他类型错误
[no-untyped-def] # 函数缺少类型注解
[var-annotated] # 变量需要类型注解
undefinedResources
资源
- Official Documentation: https://mypy.readthedocs.io/
- Type Hints PEP: https://peps.python.org/pep-0484/
- typing Module: https://docs.python.org/3/library/typing.html
- mypy GitHub: https://github.com/python/mypy
- Type Stubs: https://github.com/python/typeshed
- django-stubs: https://github.com/typeddjango/django-stubs
- FastAPI + mypy: https://fastapi.tiangolo.com/tutorial/type-hints/
- 官方文档: https://mypy.readthedocs.io/
- 类型提示PEP: https://peps.python.org/pep-0484/
- typing模块: https://docs.python.org/3/library/typing.html
- mypy GitHub: https://github.com/python/mypy
- 类型存根: https://github.com/python/typeshed
- django-stubs: https://github.com/typeddjango/django-stubs
- FastAPI + mypy: https://fastapi.tiangolo.com/tutorial/type-hints/
Related Skills
相关技能
When using mypy, consider these complementary skills (available in the skill library):
- pytest: Type-safe testing with mypy - integrates type checking into your test suite for comprehensive type coverage
- fastapi-local-dev: FastAPI with full type safety - combines FastAPI's runtime validation with mypy's static checking
- pydantic: Runtime type validation with mypy support - validates data at runtime while mypy validates at compile time
mypy Version Compatibility: This skill covers mypy 1.8+ and reflects current best practices for Python type checking in 2025.
使用mypy时,可考虑以下互补技能(技能库中已提供):
- pytest: 带mypy的类型安全测试 - 将类型检查集成到测试套件中,实现全面的类型覆盖
- fastapi-local-dev: 全类型安全的FastAPI - 结合FastAPI的运行时验证与mypy的静态检查
- pydantic: 支持mypy的运行时类型验证 - 运行时验证数据,同时mypy在编译时验证类型
mypy版本兼容性: 本技能覆盖mypy 1.8+,反映了2025年Python类型检查的当前最佳实践。