mcpField
P
python-typing

python-typing

github.com/aymericderbois开发36 次安装
代码仓库

关于 python-typing

面向 Python 3.12+ 项目的类型标注规范指南,供开发者和编码 Agent 参考。涵盖现代语法、类型检查及数据结构选择,强调明确表达类型并避免滥用 Any。

主要功能

  1. 01为公开签名添加类型标注
  2. 02采用现代 Python 类型语法
  3. 03以精确类型替代 Any
  4. 04使用泛型与 Protocol
  5. 05根据数据选择结构
  6. 06配置严格类型检查

适用场景

  1. 01统一项目的 Python 类型标注规范
  2. 02为新函数和类设计类型签名
  3. 03排查宽泛的 Any 与类型忽略
  4. 04在 CI 中落实严格类型检查

安装

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

$npx skills add https://github.com/aymericderbois/skills --skill python-typing

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

详细介绍

python-typing

变量、参数和返回值的类型标注至关重要。它可以:

  • 在运行前发现 bug
  • 为代码编写文档
  • 帮助 LLM 理解代码
  • 在 IDE 中提供更好的自动补全

目标:Python 3.12+

类型检查器(根据项目配置和已安装的工具选择):

工具+−
pyrefly (Meta, Rust)非常快速、现代较新,生态系统仍在发展中
ty (Astral, Rust)非常快速,集成于 uv/ruff仍处于预览阶段
mypy --strict成熟、广泛使用、行业标准较慢
pyright (Microsoft)快速,narrowing 能力出色 (Pylance)依赖 Node

词汇表

以下技术术语统一采用这些含义:

  • 类型标注 — 写在代码中的类型(age: int、-> User)——官方术语(PEP 526)。
  • 类型检查器 — 检查类型标注是否一致的工具(pyrefly、mypy,……)。
  • 类型推断 — 类型检查器无需显式标注而自行推导出的类型。
  • Generic — 一个传递输入类型至输出的类型参数(T)。
  • Protocol — 由其形式(方法)定义的类型,无需继承:经过验证的 鸭子类型。
  • 鸭子类型 — 根据对象能做什么来接受它,而不是根据它所属的类。
  • Any — 会在其出现的位置禁用类型检查的类型。
  • cast — 用于向 type-checker 指定类型。

规则

1. 为所有公开签名添加类型标注
  • 必须为所有函数、方法和类属性添加类型标注:参数、返回值和属性。
  • 必要时,在函数或方法内部为代码添加类型标注,以提高可读性(例如,为不够明确的函数返回值添加文档说明)。
def get_user(user_id: str) -> User | None:
    """Renvoie l'utilisateur s'il existe, None sinon."""
    ...

def do_something(v: str) -> None:
    """
    Le type de retour de process_value() n'est pas explicite, le nom de la fonction ne donne pas
    d'information claire, on peut donc typer le résultat pour améliorer la lisibilité.
    """
    result: int = process_value(v)
    ...

class UserRepository:
    user: User | None

    def __init__(self, db: Database) -> None:  # -> None explicite sur __init__
        self._db = db

    async def save(self, user: User) -> User: ...
2. 优先使用现代语法,而不是旧版 typing 别名

必须优先使用现代语法,而不是旧版 typing 别名(Optional、Union、List……)。 只有为兼容旧版 Python,才应使用旧版语法。

避免使用改为
Optional[X]X | None
Union[A, B]A | B
List[X], Dict[K, V]list[X], dict[K, V]
Tuple[int, ...], Set[X]tuple[int, ...], set[X]
from typing import List, Dict(无需导入)
3. 避免使用 Any,优先选择精确类型

Any 会在其出现的位置禁用类型检查。 在可能的情况下,将 Any 替换为更精确的类型:

  • 确实未知的数据 → object:合法,但使用前必须先收窄类型。
  • 可复用的容器或函数 → 使用 generic(规则 5),以保留类型。
  • 未标注类型的库的输出 → 在入口处使用 cast(TypeVoulu, valeur),然后在后续代码中使用明确的类型。
  • “具有某个方法的对象” → 使用 Protocol(规则 6)。

cast() 只声明类型,不会进行检查:仅用于确实已知类型的值,绝不能用来压制合理的错误——遇到这种情况,应修正实际类型。

如果必须消除某个特定错误,应明确指定代码:# type: ignore[return-value],绝不要使用裸的 # type: ignore,以免同时隐藏该行未来出现的错误。

4. 明确表示缺失

缺失是类型的一部分:使用 X | None 标注,而不是使用隐式哨兵值或未注明类型的 None。这样,类型检查器会要求先缩窄类型(is None、assert),再使用该值。

def find(user_id: str) -> User | None:  # l'absence est dans la signature
    ...


u = find("42")
u.name                    # rejeté : u peut être None
if u is not None:
    u.name                # ok : restreint à User
5. 泛型:保留类型信息

泛型可以实现适用于多种不同类型的通用类型标注。采用 PEP 695 语法(3.12+)。 通常用于将函数/方法的输入类型传递到返回类型。

def first[T](items: list[T]) -> T:
    # first([1, 2]) -> int
    # first(["a"]) -> str
    return items[0]


class Box[T]:
    def __init__(self, value: T) -> None:
        self._value = value

    def get(self) -> T:
        return self._value
6. Protocol:采用结构化类型,而非继承

Protocol 描述预期的形态(其方法),而不强制要求继承。 当你不拥有具体类,或只关心某个方法时,优先使用它。

from typing import Protocol


class Readable(Protocol):
    def read(self) -> bytes: ...


def consume(source: Readable) -> bytes:  # accepte fichier, socket, BytesIO… sans lien d'héritage
    return source.read()

只有在你拥有整个类层次结构且希望共享实现时,才使用 ABC(显式继承)。

7. 选择合适的数据结构

根据数据的性质选择容器,而不是堆叠使用 dict[str, Any]:

  • TypedDict —— 键固定且已知的字典(JSON 负载、API 响应)。
  • dataclass —— 带默认值、行为或不变量的对象。
  • NamedTuple —— 不可变且轻量的命名元组。
  • Literal / Enum —— 值的封闭集合(Literal["asc", "desc"]):类型检查器会拒绝集合之外的值,而 str 不会。
8. 命名并锁定
  • 为复杂且反复使用的类型创建别名: type Json = dict[str, "Json"] | list["Json"] | str | int | float | bool | None (PEP 695,3.12+)。有意义的名称胜过在各处重复联合类型。
  • 使用 Final 表示不可重新赋值的常量。
  • 使用 ClassVar 表示类属性(与实例属性相对)。
  • 使用 Self(3.11+)表示返回自身类型的方法(-> Self),包括子类。
  • 对预期重写其他方法的方法使用 @override(3.12+,或 typing_extensions):如果父类重命名或删除了该方法,CI 就会失败。

警示信号——停止

  • 正准备写 Any → 能否改用 object、泛型、Protocol 或有针对性的 cast?
  • 使用 # type: ignore 却未指定代码 → 添加 [code],否则会掩盖未来的错误。
  • 从 typing 导入 Optional / List / Dict → 改用现代写法(X | None、list、dict)。
  • 用 cast() 消除一个确实正确的错误 → 修正类型,而不是类型检查器。
  • 注解写得很仔细,但 CI 中没有严格的类型检查器 → 类型标注就没有约束力。

评论

支持 Markdown