关于 python-developer
用于创建和维护 Python 3.12+ 项目的开发 Skill,规定项目结构、类型标注、测试、代码检查、错误处理和安全实践,并涵盖不同版本的语言特性。
主要功能
- 01Python 3.12+ 开发指南
- 02src/ 项目布局与 pyproject.toml
- 03类型标注和现代语言特性
- 04Ruff、ty 或 mypy 与 pytest 实践
- 05安全和错误处理指南
适用场景
- 01启动新的 Python 项目
- 02按项目规范审查代码
- 03编写带类型标注的异步代码
- 04配置测试与质量检查
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ gvre/ skills --skill python-developer安装后在 Claude Code 中输入/python-developer确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/core-language-features.mdlogging-guide.mdpython-3.13-features.mdpython-3.14-features.mdREFERENCE.mdsecurity-guide.mdtesting-guide.md详细介绍
Python 开发者
编写生产级 Python 3.12+ 项目的指南,遵循当前最佳实践。
版本支持
本指南涵盖:
- Python 3.12+:基础要求和常用功能(以下所有章节)
- Python 3.13:新增功能和改进 → 请参阅 references/python-3.13-features.md
- Python 3.14:最新功能和增强 → 请参阅 references/python-3.14-features.md
功能版本矩阵
| 功能 | 3.12 | 3.13 | 3.14 |
|---|---|---|---|
类型参数语法(def func[T]()) | ✅ | ✅ | ✅ |
类型语句(type X = ...) | ✅ | ✅ | ✅ |
模式匹配(match/case) | ✅ | ✅ | ✅ |
异常组(except*) | ✅ | ✅ | ✅ |
| TaskGroup | ✅ | ✅ | ✅ |
| 用于类型收窄的 TypeIs | ❌ | ✅ | ✅ |
| ReadOnly 类型提示 | ❌ | ✅ | ✅ |
| 改进的 REPL | ❌ | ✅ | ✅ |
无括号的 except(PEP 758) | ❌ | ❌ | ✅ |
| 实验性 JIT | ❌ | ❌ | ✅ |
| 无 GIL 的自由线程模式 | ❌ | ❌ | ✅ |
快速参考
核心要求(3.12+):
- 最低要求为 Python 3.12+
- 使用 pyproject.toml 管理所有配置
- 在所有地方使用类型提示
- 使用 Ruff 进行格式化和代码检查
- 使用 ty 或 mypy 进行类型检查
- 使用 pytest 进行测试
- 采用 src/ 目录布局
类型检查器对比:
| 功能 | mypy | ty |
|---|---|---|
| 状态 | ✅ 稳定、成熟(1.0+) | ⚠️ Beta(0.0.22,2026 年 3 月) |
| 速度 | 标准 | 快 10–100 倍 |
| 语言 | Python | Rust |
| 生态系统 | 丰富 | 快速发展 |
| IDE 支持 | 通用 | VS Code、PyCharm、Neovim(内置语言服务器) |
| 可用于生产环境 | ✅ 是 | ⚠️ 正在快速成熟,适用于新项目 |
| 最适合 | 生产系统、现有代码库 | 新项目、快速迭代 |
| 维护者 | Python 社区 | Astral(Ruff/uv 的创建者) |
| 配置 | [tool.mypy] | [tool.ty] |
| 建议 | 现有代码库、风险规避型团队 | 新项目的默认选择 |
项目覆盖项
工具设置的解析方式取决于任务:
- 审查现有代码 / PR: 从代码仓库读取
pyproject.toml。 项目的实际配置是 python-version、line-length、ruff 规则、类型检查器、测试 框架和项目布局的唯一依据。不要覆盖项目 已定义的配置。 - 创建新项目: 如果尚无
pyproject.toml, 请使用以下默认值。
本地覆盖项(见“可覆盖项”)在两种模式下均适用,并且 优先级高于项目配置和以下默认值。
新项目的默认值
| 设置 | 默认值 |
|---|---|
| python-version | 3.14 |
| line-length | 120 |
| type-checker | ty |
| test-framework | pytest |
| test-directory | tests/ |
| project-layout | src/ |
| docstring-style | |
| ruff-select | E, F, W, I, B, C4, UP |
可覆盖项
覆盖项不限于上述工具默认值。代码仓库可以提供任何 影响此技能运行方式的额外上下文:
- 领域 / 业务背景: 服务的功能、集成的外部系统,以及 AI 仅阅读代码 无法获知的关键业务不变量。
- 审查行为: 要评论的优先级、要跳过的内容、置信度阈值、评论数量上限。
- 代码库状态: 正在进行的迁移、有意保留的已知技术债务、正在逐步淘汰的旧模式(以免 AI 将其标记出来)。
- 团队约定: 此代码库特有且不同于通用最佳实践的模式。
- 外部注意事项: 下游使用者、部署限制、第三方依赖中的已知限制。
如何覆盖
将覆盖规则放入你的 Agent 平台使用的本地规则文件中:
- Cursor:
.cursor/rules/python-overrides.mdc(包含globs: "**/*.py") - Claude Code: 仓库根目录下的
CLAUDE.md - 通用 / agentskills.io: 仓库根目录下的
AGENTS.md
只写入与默认规则不同的内容,或 AI 无法从代码中推断出的上下文。
覆盖示例(适用于上述任一文件):
此仓库的 Python 技能覆盖规则:
领域背景: 此服务是支付网关适配器,集成了 Stripe 和 Adyen API。错误处理和幂等性至关重要——始终指出支付流程中缺少重试逻辑或存在非幂等变更的问题。
评审行为:
- 仅指出优先级 1(严重)和优先级 2(重要)的问题
- 不要评论文档字符串风格或命名偏好
- 每个 PR 最多发表 5 条评审意见
代码库状态:
- 正在从 Flask 迁移到 FastAPI;混合使用不同模式是有意为之
legacy/目录计划在 Q3 移除;不要评审其中的文件工具覆盖项(仅当 pyproject.toml 未定义时适用):
- docstring-style:NumPy
项目结构
project-name/
├── src/
│ └── package_name/
│ ├── __init__.py
│ └── core.py
├── tests/
│ └── test_core.py
├── pyproject.toml
└── README.md
规则:
- 始终使用 src/ 布局(绝不将代码放在项目根目录)
- 包中始终包含 init.py
- 模块名使用下划线:
my_module.py,而非my-module.py
配置(pyproject.toml)
基础配置
[project]
name = "your-project"
version = "0.1.0"
description = "Your description"
requires-python = ">=3.14"
dependencies = [
"httpx>=0.28.1",
]
[project.optional-dependencies]
dev = [
"ruff>=0.14.10",
"ty>=0.0.5",
]
test = [
"pytest>=9.0.2",
"pytest-asyncio>=1.3.0",
"pytest-cov>=7.0.0",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.ruff]
line-length = 120
[tool.ruff.lint]
select = ["E", "F", "W", "I", "B", "C4", "UP"]
[tool.ty]
python-version = "3.14"
类型提示(Python 3.12+)
必须在所有地方使用:
- 所有函数参数和返回值类型
- 类属性
- 无法一目了然的模块级变量
现代语法(3.10+):
# Use | for unions (not Optional/Union)
def process(data: str | None) -> dict[str, int | float]:
...
# Use lowercase generics (not List/Dict)
def filter_items(items: list[str]) -> list[str]:
...
# Use collections.abc for abstract types
from collections.abc import Sequence, Mapping, Iterable
def process_items(items: Sequence[str]) -> Iterable[str]:
"""Process items - accepts list, tuple, or any sequence."""
return (item.upper() for item in items)
Python 3.12+ 类型特性(PEP 695):
# Type parameters with new syntax
def first[T](items: list[T]) -> T | None:
return items[0] if items else None
# Type statement for aliases
type Point = tuple[float, float]
# Generic classes
class Container[T]:
def __init__(self, value: T) -> None:
self.value = value
def get(self) -> T:
return self.value
高级模式: 有关全面的类型提示模式、Protocol、TypedDict、Literal 和重载示例,请参阅 references/core-language-features.md。
禁止事项:
- 绝不从 typing 导入
List、Dict、Tuple、Optional、Union(它们已由内置泛型和|语法取代)。仍可导入Any、Self、Protocol、TypeVar、TypeGuard、TypeIs。 - 函数绝不能缺少类型标注
现代特性
数据类(Python 3.7+,从 3.10+ 起支持 slots)
from dataclasses import dataclass
from typing import Self
@dataclass(slots=True)
class User:
id: int
name: str
email: str | None = None
def with_email(self, email: str) -> Self:
return User(id=self.id, name=self.name, email=email)
模式匹配——基础(Python 3.10+)
def handle_command(cmd: str) -> str:
match cmd:
case "quit" | "exit":
return "Exiting"
case "help":
return "Showing help"
case _:
return f"Unknown: {cmd}"
Functools 模式
from functools import cache, lru_cache
@cache # Unbounded cache for pure functions
def expensive_computation(n: int) -> int:
return n ** 2
@lru_cache(maxsize=128) # Limited cache
def fetch_data(url: str) -> dict[str, Any]:
...
Pathlib(而非 os.path)
from pathlib import Path
config_dir = Path.home() / ".config" / "myapp"
config_file = config_dir / "config.json"
上下文管理器
import os
from collections.abc import Iterator
from contextlib import contextmanager
@contextmanager
def temporary_setting(name: str, value: str) -> Iterator[None]:
old_value = os.getenv(name)
os.environ[name] = value
try:
yield
finally:
if old_value is None:
del os.environ[name]
else:
os.environ[name] = old_value
异步编程
基础异步编程(所有版本)
import asyncio
import httpx
async def fetch_url(url: str, timeout: float = 10.0) -> str:
async with httpx.AsyncClient(timeout=timeout) as client:
response = await client.get(url)
return response.text
任务组(Python 3.11+)
async def fetch_all(urls: list[str], timeout: float = 30.0) -> list[str]:
async with asyncio.timeout(timeout):
async with asyncio.TaskGroup() as tg:
tasks = [tg.create_task(fetch_url(url)) for url in urls]
return [task.result() for task in tasks]
高级模式: 有关装饰器、描述符、海象运算符、上下文变量、高级模式匹配和元类,请参阅 references/core-language-features.md。
测试(pytest)
关键模式:
- 使用 pytest(绝不使用 unittest)
- 使用参数化测试覆盖多种场景
- 模拟外部依赖
- 使用固件进行设置和清理
- 使用
@pytest.mark.asyncio标记异步测试
有关全面的测试模式、异步测试、模拟、固件和覆盖率配置,请参阅 references/testing-guide.md。
代码质量
Ruff 配置
[tool.ruff]
line-length = 120
[tool.ruff.lint]
select = ["E", "F", "W", "I", "B", "C4", "UP"]
类型检查
# ty (default for new projects)
[tool.ty]
python-version = "3.14"
# mypy (for existing projects that already use it)
[tool.mypy]
python_version = "3.14"
strict = true
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
命名约定
- 函数/变量: snake_case
- 类: PascalCase
- 常量: UPPER_SNAKE_CASE
- 私有项: 以
_为前缀
✅ 正确:calculate_average()、UserManager、MAX_RETRIES
❌ 错误:CalculateAverage()、calc_avg()、userManager
错误处理
标准异常处理(所有版本):
try:
risky_operation()
except ValueError as e:
logger.error(f"Invalid value: {e}")
except TimeoutError:
logger.warning("Operation timed out")
捕获 Exception——可接受的情况:
# ✅ Top-level handler (API endpoint, CLI) - handle gracefully
@app.route("/api/data")
def get_data():
try:
return process_data()
except Exception as e:
logger.exception("Request failed")
return {"error": "Internal server error"}, 500
# ✅ Batch processing - log and continue
for item in items:
try:
process(item)
except Exception as e:
logger.exception(f"Failed to process {item}")
continue
# ✅ Add context and wrap
except Exception as e:
raise ProcessingError(f"Failed for {item_id}") from e
# ❌ Never silently swallow
except Exception:
pass
异常组(Python 3.11+):
try:
async with asyncio.TaskGroup() as tg:
tg.create_task(task1())
tg.create_task(task2())
except* ValueError as eg:
for exc in eg.exceptions:
handle_value_error(exc)
日志记录
基础设置:
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)-8s | %(name)s - %(message)s",
)
生产环境最佳实践: 有关结构化日志记录(structlog 与 python-json-logger)、异步日志记录、关联 ID、请求跟踪和性能优化,请参阅 references/logging-guide.md。
依赖项
[project]
dependencies = [
"httpx>=0.28.1",
"pydantic>=2.5.0",
]
[project.optional-dependencies]
dev = [
"ruff>=0.14.10",
"ty>=0.0.5",
]
test = [
"pytest>=9.0.2",
"pytest-asyncio>=1.3.0",
"pytest-cov>=7.0.0",
]
反模式(绝对不要这样做)
- ❌ 可变默认参数:
def func(items=[]) - ❌ 裸
except:(会捕获 SystemExit、KeyboardInterrupt) - ❌ 默默吞掉异常:
except Exception: pass - ❌ 对用户输入使用 eval/exec
- ❌ 使用
os.path(请改用 pathlib) - ❌
from typing import List, Dict(请改用内置泛型) - ❌ 将密钥或 API 密钥硬编码
- ❌ 使用未参数化的 SQL 查询
文档
def calculate_average(
numbers: list[float],
*,
weights: list[float] | None = None
) -> float:
"""Calculate the average of numbers.
Args:
numbers: List of numbers to average
weights: Optional weights for weighted average
Returns:
The calculated average
Raises:
ValueError: If numbers is empty
Examples:
>>> calculate_average([1, 2, 3])
2.0
"""
**规则:**采用 Google 或 NumPy 文档字符串风格。为公共 API 编写文档字符串。类型应写在签名中,而非文档字符串中。
Python 3.13 和 3.14 功能
- Python 3.13:参见 references/python-3.13-features.md
- Python 3.14:参见 references/python-3.14-features.md
安全最佳实践
关键规则:
- ✅ 始终验证并清理用户输入
- ✅ 始终对 SQL 使用参数化查询
- ✅ 始终对密码进行哈希处理(Argon2 或 bcrypt)
- ✅ 始终从环境中加载密钥
- ✅ 始终对外部 API 使用 HTTPS
- ❌ 绝不硬编码密钥
- ❌ 绝不使用字符串拼接来编写 SQL
- ❌ 绝不记录敏感数据
有关输入验证、SQL 注入防护、路径遍历防护、密码哈希、密钥管理和全面安全模式,请参见 references/security-guide.md。
检查清单
面向代码作者
编写 Python 代码时:
- 使用 Python 3.12+ 功能
- 所有函数均包含类型提示
- 使用现代类型语法(
|、小写泛型、collections.abc) - 数据类使用
slots=True - 使用 Pathlib,而非 os.path
- 使用 F 字符串进行格式化
- 捕获特定异常
- 异步代码具备适当的错误处理和超时设置
- 编写测试(pytest),并使用模拟
- 遵循安全最佳实践
- 为公共 API 编写文档字符串
- 不硬编码密钥
- Ruff 和类型检查器(ty/mypy)检查通过
- 使用 src/ 布局
面向代码审查者
审查质量规则(发表评论前请遵守):
- 只有在确信时才发表评论。 如果不确定某处是否确实有问题,就不要发表评论。误报比遗漏一条建议更浪费审查者和作者的时间。
- 重信号,轻噪声。 有 2 条关键发现的审查,比提出 15 条置信度参差不齐的建议更有价值。评论应少而精。
- 默认范围:仅限 Priority 1 和 2。 除非仓库明确要求覆盖 Priority 3 问题,否则不要对此类问题发表评论。
- 绝不针对纯粹的风格偏好发表评论,尤其是配置的代码检查工具未指出的问题。若 ruff 检查通过,则风格可以接受。
- 尊重代码库的当前状态。 如果代码正处于已知迁移过程中、使用正在逐步淘汰的旧模式,或存在已记录的技术债,除非覆盖规则另有要求,否则不要指出这些模式。
- 有领域上下文时请加以利用。 支付服务缺少超时设置是关键问题;一次性迁移脚本缺少同样的超时设置则未必如此。应结合代码的实际用途权衡发现,而不只是套用通用规则。
- 遵守审查行为覆盖规则。 如果仓库设置了评论数量上限、置信度阈值或排除某些类别,请严格遵守这些约束。
- 发现是传感器数据,而非最终裁决。 绝不要因为审查结果干净就暗示改动可以安全合并;应说明你检查了什么,以及哪些内容无法检查。借来的信心本身就是风险。你可能是这个 PR 的多位审查者之一;你的审查结果干净,只能说明符合本技能关注的优先级,并不能代表整个 PR 没有问题。
- 坚守职责范围。 专注于此处定义的优先事项(Python 正确性、安全、资源安全)。不要扩展到其他审查者更适合覆盖的通用检查;独立、专业的信号比重复重叠更有价值。
审查优先级
审查 Python 代码时,先根据影响范围对每个变更文件进行分类:
- 关键(身份验证、支付、加密、数据删除、数据库迁移,以及任何处理不可信输入或个人身份信息的内容):严格审查——将临界情况的发现上调一个优先级。
- 标准(业务逻辑、API、服务层):按常规优先级规则处理。
- 低(配置、文档、生成的代码、仅测试变更):仅报告 P1;不要吹毛求疵。
然后确定发现的优先级:
优先级 1 - 关键问题(必须修复):
- 安全漏洞(SQL 注入、硬编码密钥、路径遍历、不安全的 eval、提示词注入——不可信/用户控制的文本在没有防护的情况下流入 LLM 调用;此风险潜藏于运行时数据中,无法从差异中看出)
- 正确性问题(可变默认值、异步错误、缺少超时、裸 except)
- 资源泄漏(数据库连接、文件句柄、网络套接字未使用上下文管理器)
- 新近被抑制的安全检查器检查项——本次差异中新增的
# noqa S...;将被抑制的警告视为当前问题。预先存在的抑制项不在审查范围内。 - 可疑的测试变更——断言被弱化或改写以匹配新的(可能有问题的)行为、测试被删除或跳过、变更代码路径的覆盖被移除。当差异同时修改代码及其测试时,确认测试仍然断言的是正确行为,而不只是当前行为。
优先级 2 - 重要改进(应该修复):
- 错误处理(异常范围过宽、缺少针对性、错误被吞掉)
- 类型安全(缺少类型提示、使用禁止的 typing 导入,例如
List/Optional) - 测试缺口(缺少测试、覆盖率低于 80%、未模拟外部依赖)
优先级 3 - 可改进项(可考虑修复):
- 采用现代 Python 写法(用 os.path 而非 pathlib、使用旧式格式化)
- 文档(缺少或不完整的文档字符串)
- 代码质量(命名规范、可以使用模式匹配)
- 性能(缺少 @cache、可以使用 TaskGroups)。当影响明确时,上调为优先级 2:大规模输入下的 O(n^2)、热点路径中反复进行昂贵调用,或异步代码中存在阻塞式同步 I/O。
- 重复造轮子——新代码重新实现了现有标准库或内部辅助函数,而没有复用它们。如果重新实现的辅助函数处理安全敏感逻辑,则上调为优先级 2。
- 通用安全关卡的放宽——在非关键测试上使用
@pytest.mark.skip、使用没有理由说明的无关# noqa或# type: ignore、放宽覆盖率阈值、放宽ruff或类型检查器配置。
PR 审查工作流
提示: 对于涉及多种语言的 PR,请使用 pr-reviewer 技能——它可以在一次审查中协调 Go、Python 等语言的专项审查。
收到 PR 审查请求(通过编号、URL 或分支名称)时:
步骤 1:收集上下文
- 使用
gh pr view和gh pr diff获取 PR 描述、元数据和差异 - 快速筛查: 过于庞大的差异、大量重写测试或含糊/缺失意图说明本身就是问题。应标记“PR 过大,无法有把握地审查——建议拆分”,而不是草率通过。
- 阅读
pyproject.toml,了解项目实际的工具配置 - 阅读任何本地覆盖规则(
.cursor/rules/、AGENTS.md、CLAUDE.md) - 获取现有审查评论(
gh pr view --comments),跳过其他审查者或作者已经提出的问题——不要重复报告 - 根据 PR 描述,确定变更的文件及其用途
步骤 2:审查
- 应用上述审查质量规则和优先级
- 根据 PR 声明的意图评估变更——指出偏离 PR 所述目标的地方,而不只是泛泛的代码质量问题
- 忽略本地覆盖规则排除的文件(例如冻结目录)
第 3 步:呈现发现
- 按优先级分组向用户展示发现,并提供文件路径和 行号
- 在用户明确批准之前,切勿发布到 GitHub
- 如果没有值得标记的问题,请说明这一点——审查结果无问题也是 有效结论
第 4 步:发布(仅在获得批准后)
- 将每项发现作为单独的行内审查评论,发布到 PR 中的具体 行
- 使用单次审查提交(而不是逐条评论),这样 作者只会收到一条通知,而不是 N 条
- 提交中立的
COMMENT审查,而不是REQUEST_CHANGES。由用户通过下面的后续命令决定如何处理。
后续命令
无需重复完整上下文即可使用:
- "re-review" — 获取最新更改,仅审查新增或修改的 代码块,并呈现发现
- "approve" — 添加建设性评论并批准 PR
- "request changes" — 提交审查并请求更改,同时总结尚未解决的问题
自主模式
在没有人工参与的情况下运行(CI、GitHub Actions、机器人)时, 采用以下默认设置,而非上面的交互步骤:
- 范围:仅限优先级 1 和 2。 不要评论 P3 问题。
- 每个 PR 最多 5 条审查评论。 如果发现问题超过 5 个, 发布最关键的 5 个,并在审查摘要中注明其余问题的数量。
- 直接发布——跳过预览步骤(第 3 步)。此时没有用户需要批准。
- 提交为
COMMENT,绝不使用REQUEST_CHANGES或APPROVE。 - 如果没有发现 P1 问题,则不要发布任何审查。 对于自主审查器,沉默即表示认可。
- 对同一 PR 的新提交,仅重新审查发生更改的代码块。
仓库级覆盖设置可以放宽或收紧这些默认值(例如,纳入 P2、提高或降低评论数量上限)。
注意: 要发布到 GitHub,必须安装并完成身份验证的
ghCLI。
资源
- 完整参考: 参见 references/REFERENCE.md
- 核心语言功能: 参见 references/core-language-features.md
- 日志指南: 参见 references/logging-guide.md
- 测试指南: 参见 references/testing-guide.md
- 安全指南: 参见 references/security-guide.md
- Python 3.13 功能: 参见 references/python-3.13-features.md
- Python 3.14 功能: 参见 references/python-3.14-features.md