mcpField
P
python-best-practices

python-best-practices

github.com/ofershap开发139 次安装36 周前更新
代码仓库

关于 python-best-practices

python-best-practices 是面向 Python 3.12 及以上代码编写与审查的技能,供需要遵循现代 Python 惯例的编码 Agent 使用。内容涵盖类型标注、异步模式、Pydantic v2、项目配置和标准库选择。

主要功能

  1. 01采用现代内置类型标注语法
  2. 02使用 Pydantic v2 API
  3. 03以 uv 和 pyproject.toml 管理新项目
  4. 04使用 pathlib 与标准库 tomllib
  5. 05采用 asyncio.TaskGroup 结构化并发

适用场景

  1. 01编写符合 Python 3.12+ 的类型化代码
  2. 02审查并更新旧式 Pydantic 写法
  3. 03为新项目配置依赖与项目元数据
  4. 04处理异步任务及 TOML 文件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ofershap/python-best-practices --skill python-best-practices

安装后在 Claude Code 中输入/python-best-practices确认已加载。

详细介绍

适用场景

编写或审查面向 Python 3.12+ 的 Python 代码时,请使用此技能。它要求使用现代类型提示、异步模式、Pydantic v2 API、项目结构和标准库。基于旧代码库训练的 Agent 往往会生成过时的模式;此技能会纠正这些问题。

关键规则

1. 使用现代类型提示语法

错误:

from typing import Union, List, Dict, Optional

def process(items: List[str]) -> Optional[Dict[str, Union[int, str]]]:
    ...

正确:

def process(items: list[str]) -> dict[str, int | str] | None:
    ...

原因: list[str] 和 X | Y 是 Python 3.9+ 内置的功能(PEP 585、PEP 604)。导入 typing.List、typing.Union、typing.Optional 来表示内置泛型既冗长,也已被弃用。

2. 使用类型参数语法(PEP 695,Python 3.12+)

错误:

from typing import TypeVar

T = TypeVar("T")

def max(args: list[T]) -> T:
    ...

正确:

def max[T](args: list[T]) -> T:
    ...

原因: PEP 695 类型参数更简洁,且作用域局限于函数或类。泛型类也同样如此:使用 class Bag[T]:,而不是 class Bag(Generic[T]):。

3. 使用 match/case 代替 if/elif 链进行模式匹配

错误:

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"

正确:

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. 使用 Pydantic v2 API,切勿使用 v1

错误:

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):
        ...

正确:

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 的装饰器和 class Config 已弃用。v2 使用 model_config、field_validator、model_validator,并显式指定模式。

5. 在新项目中使用 uv 进行包管理

错误:

pip install -r requirements.txt
poetry init

正确:

uv init
uv add requests pydantic
uv sync

原因: uv 速度快,采用现代锁文件,并原生支持 pyproject.toml。新项目优先使用它。

6. 使用 pyproject.toml 配置项目

错误:

# setup.py
from setuptools import setup
setup(name="myapp", version="0.1", ...)

正确:

# pyproject.toml
[project]
name = "myapp"
version = "0.1"
dependencies = ["requests"]

原因: setup.py 和 setup.cfg 已属旧式配置。pyproject.toml(PEP 517/518)是标准做法。

7. 使用 pathlib.Path 代替 os.path

错误:

import os
path = os.path.join(os.getcwd(), "data", "file.txt")
if os.path.exists(path):
    with open(path) as f:
        ...

正确:

from pathlib import Path
path = Path.cwd() / "data" / "file.txt"
if path.exists():
    path.read_text()

原因: pathlib 采用面向对象的方式,更清晰且支持跨平台。对于简单的读写,优先使用 Path.read_text()/write_text(),而不是 open()。

8. 使用 f-string 代替 .format() 或 % 格式化

错误:

"User %s has %d items" % (name, count)
"User {} has {} items".format(name, count)

正确:

f"User {name} has {count} items"

原因: f-string 速度更快,也更易读。

9. 使用 dataclass 或 Pydantic 表示结构化数据,不要使用普通 dict

错误:

def get_user() -> dict:
    return {"name": "Alice", "age": 30}

正确:

from dataclasses import dataclass

@dataclass
class User:
    name: str
    age: int

def get_user() -> User:
    return User(name="Alice", age=30)

原因: 结构化类型可提供类型安全和 IDE 支持。需要验证时使用 Pydantic。

10. 使用 asyncio.TaskGroup 代替 asyncio.gather(Python 3.11+)

错误:

import asyncio
results = await asyncio.gather(f1(), f2(), f3())

正确:

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. 使用异常组和 except*(Python 3.11+)

错误:

try:
    ...
except (ValueError, TypeError) as e:
    ...

正确(处理并发产生的 ExceptionGroup 时):

try:
    ...
except* TypeError as e:
    print(f"caught {type(e)} with nested {e.exceptions}")
except* OSError as e:
    ...

原因: except* 用于处理来自 asyncio 和并发任务的 ExceptionGroup。捕获 TaskGroup 或类似结构抛出的异常时应使用它。

12. 使用 tomllib 解析 TOML(Python 3.11+ 标准库)

错误:

import toml
data = toml.load("pyproject.toml")

正确:

import tomllib
with open("pyproject.toml", "rb") as f:
    data = tomllib.load(f)

原因: tomllib 是内置模块,无需额外依赖。读取时使用二进制模式。

13. 使用 typing.override 装饰器(Python 3.12+)

错误:

class Child(Parent):
    def method(self) -> str:
        return "child"

正确:

from typing import override

class Child(Parent):
    @override
    def method(self) -> str:
        return "child"

原因: @override 能明确表达意图,并发现方法名中的拼写错误。

14. 使用带守卫条件的结构化模式匹配

错误:

match x:
    case (a, b):
        if a > b:
            ...

正确:

match x:
    case (a, b) if a > b:
        ...

原因: 守卫条件(if)可将逻辑保留在模式中,并避免嵌套条件判断。

15. 对抽象类型优先使用 collections.abc,而不是 typing

错误:

from typing import Iterable, Mapping

正确:

from collections.abc import Iterable, Mapping

原因: collections.abc 是 ABC 的规范来源。typing 虽会重新导出这些类型,但运行时检查优先使用 collections.abc。

模式

带类型参数的泛型类
class Bag[T]:
    def __iter__(self) -> Iterator[T]:
        ...
    def add(self, arg: T) -> None:
        ...
泛型类型别名
type ListOrSet[T] = list[T] | set[T]
使用 before 模式的 Pydantic model_validator
@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 data
匹配多个字面值
case 401 | 403 | 404:
    return "Not allowed"

反模式

  • 对于内置泛型,切勿使用 from typing import List, Dict, Union, Optional;应使用 list、 dict、X | Y、X | None。
  • 使用 Pydantic 时,切勿使用 @validator、@root_validator 或 class Config;应使用 v2 API。
  • 新代码中切勿使用 os.path;应使用 pathlib.Path。
  • 切勿使用 setup.py 或 setup.cfg;应使用 pyproject.toml。
  • 有 f-string 可用时,切勿使用 % 或 .format()。
  • 切勿使用 toml 包;应使用标准库 tomllib(Python 3.11+)。
  • 适用 Python 3.12+ 类型参数语法时,简单泛型切勿使用 TypeVar。
  • 有 TaskGroup 可用时,结构化并发切勿使用 asyncio.gather(3.11+)。
  • 适合使用 dataclass 或 Pydantic 模型时,切勿使用普通 dict 表示带类型的结构化数据。
  • 切勿使用 from typing import Iterable, Mapping;应使用 from collections.abc import ...。

评论

支持 Markdown