python-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWhen to use
适用场景
Use this skill when writing or reviewing Python code targeting Python 3.12+. It enforces modern type
hints, async patterns, Pydantic v2 API, project structure, and standard library usage. Agents
trained on older codebases often emit outdated patterns; this skill corrects that.
当你编写或审查针对Python 3.12+的Python代码时,可以使用本技能。它会强制采用现代类型提示、异步模式、Pydantic v2 API、项目结构以及标准库用法。基于旧代码库训练的Agent通常会输出过时的模式,本技能可以纠正这一问题。
Critical Rules
核心规则
1. Use modern type hint syntax
1. 使用现代类型提示语法
Wrong:
python
from typing import Union, List, Dict, Optional
def process(items: List[str]) -> Optional[Dict[str, Union[int, str]]]:
...Correct:
python
def process(items: list[str]) -> dict[str, int | str] | None:
...Why: and are built-in in Python 3.9+ (PEP 585, PEP 604). Importing
, , is verbose and deprecated for built-in generics.
list[str]X | Ytyping.Listtyping.Uniontyping.Optional错误示例:
python
from typing import Union, List, Dict, Optional
def process(items: List[str]) -> Optional[Dict[str, Union[int, str]]]:
...正确示例:
python
def process(items: list[str]) -> dict[str, int | str] | None:
...原因: 和 在Python 3.9+中已内置(符合PEP 585、PEP 604规范)。导入、、的方式冗余且已被弃用,不适用于内置泛型类型。
list[str]X | Ytyping.Listtyping.Uniontyping.Optional2. Use type parameter syntax (PEP 695, Python 3.12+)
2. 使用类型参数语法(PEP 695,Python 3.12+)
Wrong:
python
from typing import TypeVar
T = TypeVar("T")
def max(args: list[T]) -> T:
...Correct:
python
def max[T](args: list[T]) -> T:
...Why: PEP 695 type parameters are simpler and local to the function/class. Same for generic
classes: instead of .
class Bag[T]:class Bag(Generic[T]):错误示例:
python
from typing import TypeVar
T = TypeVar("T")
def max(args: list[T]) -> T:
...正确示例:
python
def max[T](args: list[T]) -> T:
...原因: PEP 695定义的类型参数语法更简洁,且作用域局限于函数/类。泛型类同理:应使用而非。
class Bag[T]:class Bag(Generic[T]):3. Use match/case instead of if/elif chains for pattern matching
3. 使用match/case替代if/elif链进行模式匹配
Wrong:
python
def http_error(status: int) -> str:
if status == 400:
return "Bad request"
elif status == 404:
return "Not found"
elif status == 418:
return "I'm a teapot"
else:
return "Something's wrong"Correct:
python
def http_error(status: int) -> str:
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Something's wrong"Why: Structural pattern matching (PEP 634) is clearer for disjoint cases and supports
destructuring. Use for multiple literals.
case 401 | 403 | 404:错误示例:
python
def http_error(status: int) -> str:
if status == 400:
return "Bad request"
elif status == 404:
return "Not found"
elif status == 418:
return "I'm a teapot"
else:
return "Something's wrong"正确示例:
python
def http_error(status: int) -> str:
match status:
case 400:
return "Bad request"
case 404:
return "Not found"
case 418:
return "I'm a teapot"
case _:
return "Something's wrong"原因: 结构化模式匹配(PEP 634)在处理互斥分支时更清晰,还支持解构操作。可使用匹配多个字面量。
case 401 | 403 | 404:4. Use Pydantic v2 API, never v1
4. 使用Pydantic v2 API,禁止使用v1版本
Wrong:
python
from pydantic import BaseModel, validator, root_validator
class Model(BaseModel):
x: list[int]
class Config:
validate_assignment = True
@validator("x", each_item=True)
def validate_x(cls, v):
return v * 2
@root_validator
def check_a_b(cls, values):
...Correct:
python
from pydantic import BaseModel, field_validator, model_validator, ConfigDict
class Model(BaseModel):
model_config = ConfigDict(validate_assignment=True)
x: list[int]
@field_validator("x", mode="each")
@classmethod
def validate_x(cls, v: int) -> int:
return v * 2
@model_validator(mode="after")
def check_a_b(self) -> "Model":
...
return selfWhy: Pydantic v1 decorators and are deprecated. v2 uses ,
, with explicit modes.
class Configmodel_configfield_validatormodel_validator错误示例:
python
from pydantic import BaseModel, validator, root_validator
class Model(BaseModel):
x: list[int]
class Config:
validate_assignment = True
@validator("x", each_item=True)
def validate_x(cls, v):
return v * 2
@root_validator
def check_a_b(cls, values):
...正确示例:
python
from pydantic import BaseModel, field_validator, model_validator, ConfigDict
class Model(BaseModel):
model_config = ConfigDict(validate_assignment=True)
x: list[int]
@field_validator("x", mode="each")
@classmethod
def validate_x(cls, v: int) -> int:
return v * 2
@model_validator(mode="after")
def check_a_b(self) -> "Model":
...
return self原因: Pydantic v1的装饰器和已被弃用。v2版本使用、、并支持显式指定模式。
class Configmodel_configfield_validatormodel_validator5. Use uv for package management in new projects
5. 新项目使用uv进行包管理
Wrong:
bash
pip install -r requirements.txt
poetry initCorrect:
bash
uv init
uv add requests pydantic
uv syncWhy: uv is fast, has a modern lockfile, and supports pyproject.toml natively. Prefer it for new
projects.
错误示例:
bash
pip install -r requirements.txt
poetry init正确示例:
bash
uv init
uv add requests pydantic
uv sync原因: uv速度快,拥有现代锁文件,原生支持pyproject.toml。新项目优先选择它。
6. Use pyproject.toml for project configuration
6. 使用pyproject.toml进行项目配置
Wrong:
python
undefined错误示例:
python
undefinedsetup.py
setup.py
from setuptools import setup
setup(name="myapp", version="0.1", ...)
**Correct:**
```tomlfrom setuptools import setup
setup(name="myapp", version="0.1", ...)
**正确示例:**
```tomlpyproject.toml
pyproject.toml
[project]
name = "myapp"
version = "0.1"
dependencies = ["requests"]
**Why:** setup.py and setup.cfg are legacy. pyproject.toml (PEP 517/518) is the standard.[project]
name = "myapp"
version = "0.1"
dependencies = ["requests"]
**原因:** setup.py和setup.cfg属于遗留方案。pyproject.toml(符合PEP 517/518规范)是当前的标准配置文件。7. Use pathlib.Path instead of os.path
7. 使用pathlib.Path替代os.path
Wrong:
python
import os
path = os.path.join(os.getcwd(), "data", "file.txt")
if os.path.exists(path):
with open(path) as f:
...Correct:
python
from pathlib import Path
path = Path.cwd() / "data" / "file.txt"
if path.exists():
path.read_text()Why: pathlib is object-oriented, clearer, and cross-platform. Prefer
/ over for simple reads/writes.
Path.read_text()write_text()open()错误示例:
python
import os
path = os.path.join(os.getcwd(), "data", "file.txt")
if os.path.exists(path):
with open(path) as f:
...正确示例:
python
from pathlib import Path
path = Path.cwd() / "data" / "file.txt"
if path.exists():
path.read_text()原因: pathlib采用面向对象设计,更清晰且跨平台。对于简单的读写操作,优先使用/而非。
Path.read_text()write_text()open()8. Use f-strings instead of .format() or % formatting
8. 使用f-string替代.format()或%格式化
Wrong:
python
"User %s has %d items" % (name, count)
"User {} has {} items".format(name, count)Correct:
python
f"User {name} has {count} items"Why: f-strings are faster and more readable.
错误示例:
python
"User %s has %d items" % (name, count)
"User {} has {} items".format(name, count)正确示例:
python
f"User {name} has {count} items"原因: f-string速度更快,可读性更强。
9. Use dataclasses or Pydantic for structured data, not plain dicts
9. 使用dataclasses或Pydantic处理结构化数据,避免使用普通字典
Wrong:
python
def get_user() -> dict:
return {"name": "Alice", "age": 30}Correct:
python
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
def get_user() -> User:
return User(name="Alice", age=30)Why: Structured types give type safety and IDE support. Use Pydantic when you need validation.
错误示例:
python
def get_user() -> dict:
return {"name": "Alice", "age": 30}正确示例:
python
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
def get_user() -> User:
return User(name="Alice", age=30)原因: 结构化类型提供类型安全和IDE支持。当需要验证功能时,使用Pydantic。
10. Use asyncio.TaskGroup instead of asyncio.gather (Python 3.11+)
10. 使用asyncio.TaskGroup替代asyncio.gather(Python 3.11+)
Wrong:
python
import asyncio
results = await asyncio.gather(f1(), f2(), f3())Correct:
python
import asyncio
async with asyncio.TaskGroup() as tg:
t1 = tg.create_task(f1())
t2 = tg.create_task(f2())
t3 = tg.create_task(f3())
results = (t1.result(), t2.result(), t3.result())Why: TaskGroup propagates exceptions correctly and cancels other tasks on failure.
错误示例:
python
import asyncio
results = await asyncio.gather(f1(), f2(), f3())正确示例:
python
import asyncio
async with asyncio.TaskGroup() as tg:
t1 = tg.create_task(f1())
t2 = tg.create_task(f2())
t3 = tg.create_task(f3())
results = (t1.result(), t2.result(), t3.result())原因: TaskGroup可以正确传播异常,并在任务失败时取消其他任务。
11. Use exception groups and except* (Python 3.11+)
11. 使用异常组和except*(Python 3.11+)
Wrong:
python
try:
...
except (ValueError, TypeError) as e:
...Correct (when dealing with ExceptionGroup from concurrency):
python
try:
...
except* TypeError as e:
print(f"caught {type(e)} with nested {e.exceptions}")
except* OSError as e:
...Why: handles ExceptionGroups from asyncio and concurrent tasks. Use when catching from
TaskGroup or similar.
except*错误示例:
python
try:
...
except (ValueError, TypeError) as e:
...正确示例(处理并发场景中的ExceptionGroup时):
python
try:
...
except* TypeError as e:
print(f"caught {type(e)} with nested {e.exceptions}")
except* OSError as e:
...原因: 用于处理来自asyncio和并发任务的ExceptionGroup。在捕获TaskGroup或类似组件抛出的异常时使用。
except*12. Use tomllib for TOML parsing (Python 3.11+ stdlib)
12. 使用tomllib解析TOML(Python 3.11+标准库)
Wrong:
python
import toml
data = toml.load("pyproject.toml")Correct:
python
import tomllib
with open("pyproject.toml", "rb") as f:
data = tomllib.load(f)Why: tomllib is built-in; no extra dependency. Read in binary mode.
错误示例:
python
import toml
data = toml.load("pyproject.toml")正确示例:
python
import tomllib
with open("pyproject.toml", "rb") as f:
data = tomllib.load(f)原因: tomllib是内置库,无需额外依赖。需以二进制模式读取文件。
13. Use typing.override decorator (Python 3.12+)
13. 使用typing.override装饰器(Python 3.12+)
Wrong:
python
class Child(Parent):
def method(self) -> str:
return "child"Correct:
python
from typing import override
class Child(Parent):
@override
def method(self) -> str:
return "child"Why: @override makes intent explicit and catches typos in method names.
错误示例:
python
class Child(Parent):
def method(self) -> str:
return "child"正确示例:
python
from typing import override
class Child(Parent):
@override
def method(self) -> str:
return "child"原因: @override可以明确表达重写父类方法的意图,并能捕获方法名拼写错误。
14. Use structural pattern matching with guards
14. 使用带守卫条件的结构化模式匹配
Wrong:
python
match x:
case (a, b):
if a > b:
...Correct:
python
match x:
case (a, b) if a > b:
...Why: Guards () keep logic in the pattern and avoid nested conditionals.
if错误示例:
python
match x:
case (a, b):
if a > b:
...正确示例:
python
match x:
case (a, b) if a > b:
...原因: 守卫条件()将逻辑整合到模式中,避免嵌套条件判断。
if15. Prefer collections.abc over typing for abstract types
15. 优先使用collections.abc而非typing中的抽象类型
Wrong:
python
from typing import Iterable, MappingCorrect:
python
from collections.abc import Iterable, MappingWhy: collections.abc is the canonical source for ABCs. typing re-exports them but
collections.abc is preferred for runtime checks.
错误示例:
python
from typing import Iterable, Mapping正确示例:
python
from collections.abc import Iterable, Mapping原因: collections.abc是抽象基类(ABC)的标准来源。typing模块虽然也导出这些类,但在运行时检查时,优先使用collections.abc。
Patterns
推荐模式
Generic class with type parameter
带类型参数的泛型类
python
class Bag[T]:
def __iter__(self) -> Iterator[T]:
...
def add(self, arg: T) -> None:
...python
class Bag[T]:
def __iter__(self) -> Iterator[T]:
...
def add(self, arg: T) -> None:
...Generic type alias
泛型类型别名
python
type ListOrSet[T] = list[T] | set[T]python
type ListOrSet[T] = list[T] | set[T]Pydantic model_validator before mode
Pydantic的model_validator前置模式
python
@model_validator(mode="before")
@classmethod
def check_card_number_not_present(cls, data: Any) -> Any:
if isinstance(data, dict) and "card_number" in data:
raise ValueError("'card_number' should not be included")
return datapython
@model_validator(mode="before")
@classmethod
def check_card_number_not_present(cls, data: Any) -> Any:
if isinstance(data, dict) and "card_number" in data:
raise ValueError("'card_number' should not be included")
return dataMatch with multiple literals
匹配多个字面量
python
case 401 | 403 | 404:
return "Not allowed"python
case 401 | 403 | 404:
return "Not allowed"Anti-Patterns
反模式
- Never use for built-in generics; use
from typing import List, Dict, Union, Optional,list,dict,X | Y.X | None - Never use or
@validatoror@root_validatorwith Pydantic; use v2 API.class Config - Never use for new code; use
os.path.pathlib.Path - Never use or
setup.py; usesetup.cfg.pyproject.toml - Never use or
%when f-strings are available..format() - Never use package; use stdlib
toml(Python 3.11+).tomllib - Never use for simple generics when Python 3.12+ type parameter syntax applies.
TypeVar - Never use for structured concurrency when TaskGroup is available (3.11+).
asyncio.gather - Never use plain dicts for typed structured data when dataclasses or Pydantic models fit.
- Never use ; use
from typing import Iterable, Mapping.from collections.abc import ...
- 永远不要对内置泛型类型使用;应使用
from typing import List, Dict, Union, Optional、list、dict、X | Y。X | None - 永远不要在Pydantic中使用、
@validator或@root_validator;应使用v2版本API。class Config - 永远不要在新代码中使用;应使用
os.path。pathlib.Path - 永远不要使用或
setup.py;应使用setup.cfg。pyproject.toml - 当可以使用f-string时،永远不要使用或
%。.format() - 永远不要使用包;应使用标准库中的
toml(Python 3.11+)。tomllib - 当Python 3.12+的类型参数语法适用时,永远不要对简单泛型使用。
TypeVar - 当TaskGroup可用时(Python 3.11+),永远不要使用进行结构化并发。
asyncio.gather - 当dataclasses或Pydantic模型适用时,永远不要使用普通字典处理带类型的结构化数据。
- 永远不要使用;应使用
from typing import Iterable, Mapping。from collections.abc import ...