关于 python
为编写或修改 Python 3.11+ 代码的开发者提供约定与工具指导,涵盖类型标注、数据建模、异步任务、测试和打包,并给出严格的 mypy 配置基线。
主要功能
- 01为公开函数签名添加类型
- 02优先使用 Protocol 和数据类
- 03使用 pathlib 和安全默认值
- 04通过 TaskGroup 管理异步任务
- 05使用 pytest fixture 与参数化
- 06配置严格的 mypy 和 Ruff
适用场景
- 01为新 Python 代码采用一致约定
- 02检查变更中的类型与异步安全性
- 03建立打包、lint 和测试实践
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ alexander-danilenko/ cortex-ai-skills --skill python安装后在 Claude Code 中输入/python确认已加载。
详细介绍
Python
Python 3.11+ 的项目约定。编写或修改代码时请遵循这些约定;除非有人要求,否则不要重构未改动的文件来匹配这些约定。
约定
-
为每个公开签名添加类型标注,并确保
mypy --strict检查通过。内部代码可选添加类型标注;边界处的类型标注能阻止调用方传入错误的内容。 -
使用内置泛型和
X | None:list[str]、dict[str, int]、str | None。typing.List和Optional[str]是 Python 3.10 之前的写法,只会增加一次导入。 -
优先使用
Protocol,而非继承 ABC。 结构化类型允许任何形状正确的对象满足契约(包括测试替身),无需维护继承树。 -
使用数据类存放数据,适用时设置
slots=True和frozen=True。它们能正确生成__init__、__repr__和__eq__;手写__init__往往会导致字段不同步。Pydantic 用于边界处的验证和(反)序列化,而非普通记录。 -
使用
pathlib,而非os.path。 运算符拼接路径时,不会因多余的分隔符而悄悄生成错误路径。 -
绝不使用可变默认参数。
def f(items=[])会让每次调用共享同一个列表——这是一个只有第二次调用时才会显现的 bug。默认值设为None,并在函数内部创建对象。 -
优先使用
asyncio.TaskGroup,而非直接使用gather(3.11+):任务失败时,它会取消同组任务,并通过ExceptionGroup报告错误,因此崩溃的任务不会让其余任务脱离管理、继续运行。设置截止时间时使用async with asyncio.timeout(n)。 -
为每个
create_task返回值保留引用。 事件循环只持有弱引用,因此即发即弃的任务可能在执行过程中被垃圾回收,然后直接消失——没有错误,也没有结果。将它们保存在set中,并在任务完成时移除:_tasks: set[asyncio.Task[None]] = set() def spawn(coro: Coroutine[None, None, None]) -> None: task = asyncio.create_task(coro) _tasks.add(task) task.add_done_callback(_tasks.discard) -
绝不使用裸
except:——它会吞掉KeyboardInterrupt和SystemExit。只捕获你确实能够处理的异常。 -
为公开函数和类编写 Google 风格的文档字符串,说明其意图,而非重复签名。完整规则见
documentation技能。 -
pytest:用夹具进行设置,用
parametrize覆盖用例。 在单个测试中使用循环,只会报告一次失败,并隐藏出问题的用例;parametrize会为每个用例命名。 -
使用 Poetry 和
pyproject.toml进行打包,使用 ruff 进行 lint 和格式化,并在类型应供使用者查看的包中加入py.typed标记。 使用src/布局——它能防止测试导入工作目录而非已安装的包;如果导入了工作目录,有问题的包也可能通过自己的测试套件。
工具基线
strict = true 已涵盖大部分检查;以下设置会补充那些能发现实际 bug、而非仅检查风格的检查。--strict-markers 比看上去更重要:没有它,拼错的 @pytest.mark.integraton 会被静默忽略,测试仍会在你以为已排除它的情况下运行。
[tool.mypy]
strict = true
warn_unreachable = true
warn_redundant_casts = true
warn_unused_ignores = true
[[tool.mypy.overrides]]
module = "untyped_dep.*"
ignore_missing_imports = true
[tool.pytest.ini_options]
addopts = ["-ra", "--strict-markers", "--strict-config"]