关于 python
python 是一套面向现代 Python 3.12+ 项目的编码规范,供开发者统一类型标注、模块组织和分层应用设计。
主要功能
- 01按领域组织模块
- 02分层依赖与精简入口
- 03类型标注和现代语法
- 04命名与导入规范
- 05异常和日志指导
适用场景
- 01组织新的 Python 应用
- 02定义可复用的业务逻辑服务
- 03检查函数签名和类型标注
- 04减少循环导入和模块耦合
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ imankulov/ skills --skill python安装后在 Claude Code 中输入/python确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/data-structures.mddocstrings.mdlogical-grouping.mdmodule-organization.mdparallel-symmetry.mdurl-parsing.md详细介绍
Python 编码规范
面向现代 Python(3.12+)的一组约定,强调类型安全、清晰的函数签名、一致的模块组织和分层架构。
应用架构
醒目架构
按领域命名模块——billing/、authentication/、annual_reports/——这样,目录列表展示的就是应用的功能,而不是使用的框架。目录承载领域;下方的层放在目录内的文件中(billing/services.py、billing/api.py)。顶层的 models/ 或 views/ 目录会颠倒这种结构,把同一领域分散到整个目录树中。
保持模块扁平——让 auth/、users/ 和 billing/ 成为同级目录,而不是使用 users/auth/ 和 users/billing/。嵌套会把领域埋到下一层,因此从顶层目录列表无法一眼看出应用的用途。
分层
依赖向下流动。每一层都从下方的层导入,绝不反向导入。
+-------------------------------------------------------+
| api.py, tasks.py, admin.py, ... (entry points) |
+-------------------------------------------------------+
| imports
v
+-------------------------------------------------------+
| services.py (business logic) |
+-------------------------------------------------------+
| imports
v
+-------------------------------------------------------+
| models.py, types.py, const.py (data layer) |
+-------------------------------------------------------+
入口点(api.py、tasks.py、admin.py、cli.py 等)是调用服务函数的轻量封装。它们负责解析请求、调度任务和格式化输出,但不包含业务逻辑。入口点之间绝不相互导入——它们是平级模块,都依赖服务;交叉导入会造成循环依赖和紧密耦合。
services.py 存放所有业务逻辑。服务函数可供任意入口点复用——API 端点、后台任务、CLI 命令和管理操作都可以调用同一个服务函数。
服务接收结构化输入并返回结构化输出。它可以执行工作所需的任何 I/O——读取通过路径提供的文件、查询数据库、调用第三方 API——但绝不处理面向用户的边界:不进行提示或参数解析,不将原始字符串强制转换为类型化值,不访问 HTTP 请求或其他框架对象,也不格式化结果以供展示。所有这些都属于封装层。
数据层(models.py、types.py、const.py)定义不含业务逻辑的数据结构。const.py 不得包含内部导入,因此可以安全地从任何位置导入。
将所有导入放在模块顶部。只有为打破循环依赖时,才在函数内延迟导入;如果经常需要这样做,通常说明模块结构需要重构。
入口点命名约定
服务函数使用基础名称。封装它们的入口点会添加对应层的后缀,这样在任何地方都可以使用 from myapp.services import create_user,无需别名。
| 层 | 后缀 | 示例 |
|---|---|---|
| 服务(业务逻辑) | (无) | create_user() |
| API 端点 | _api | create_user_api() |
| CLI 命令 | _cli | create_user_cli() |
| 后台任务 | _task | create_user_task() |
函数
- 如果类型能传达信息,就为参数和返回值添加类型注解。如果唯一诚实的类型是
Any(可选或动态导入库的封装器、通用装饰器、透传的**kwargs),就不要添加注解——Any只会增加噪声,对读者、类型检查器或 IDE 都没有帮助。 - 文档字符串不承载类型信息——类型应写在签名中。关于应写多少内容,请参阅文档字符串和注释。
- 对于参数超过 3 个的函数,使用仅限关键字参数(
*)。 - 内部辅助函数以下划线为前缀,并将其文档字符串保持为一行。
- 如果辅助函数只有一个调用者,就将它内联,除非它包含值得单独测试的逻辑——谓词、映射器或转换函数。为这些逻辑命名后,测试就能用手动构造的输入来驱动它们,而不必搭建调用者所需的一切;否则,给一个函数体再起一个名字毫无益处。
- 按照自上而下的报纸式顺序组织文件(Robert C. Martin 的 Clean Code):先放公共函数,再放辅助函数;只有一个调用者的
_helper紧跟在调用者之后。将 Pydantic 模型放在靠前的位置,确保它们在使用前已定义。
下面的扩展文档字符串形式展示了函数在确实需要全部三个部分时的样子;大多数函数只写一行摘要。
def process_user_data(*, user_id: int, name: str, email: str, age: int) -> UserData:
"""Process and validate user data.
Args:
user_id: Unique identifier for the user
name: User's full name
email: User's email address
age: User's age in years
Returns:
Processed user data with validation status
Raises:
ValueError: If the email is malformed or the age is negative
"""
...
def _format_name(first: str, last: str) -> str:
"""Combine first and last name into full name."""
return f"{first} {last}".strip()
异常
服务函数通过抛出异常来表示失败。在文档字符串的 Raises: 部分列出函数有意抛出的每个异常——入口点会读取这份约定,以决定捕获哪些异常以及如何转换它们。
优先使用内置异常:输入错误用 ValueError,不变量遭到破坏用 RuntimeError。如果应用需要自定义异常,就在 myapp/exceptions.py 中定义一个异常层次结构——与应用的 __init__.py 放在一起,位于会抛出这些异常的模块的上一级;以 MyAppError 为基类,并让 MyAppAuthenticationError 等异常继承它。模块不应各自拥有 exceptions.py,而且一种失败情况很少值得单独定义一个类;调用方捕获基类的情况远多于捕获叶子类。
前导下划线
单个前导下划线表示“内部使用——不要从此模块外部调用或导入”。仅在外部使用会造成实际问题的以下两种情况下使用:
- 内部函数和方法。
_helper可能会跳过公共 API 要求的验证、依赖外围函数的不变量,或随时改变形式。 - 用于延迟初始化的可变单例状态(
_queue、_started、_patched、_server_url)。外部代码修改这些状态会破坏模块的不变量。
如果另一个模块需要导入它,它就不是内部对象——去掉下划线。from foo import _bar 是在撒谎:要么让该函数真正保持内部使用,要么将它移到共享模块并移除前缀。将辅助函数提取到共享的 utils.py 时,去掉下划线——它只是在旧模块中属于内部对象,并不属于新模块。
不要为模块级常量或类添加前缀。UPPER_CASE 已经表示“常量”;类的私有性取决于它所在的位置(不要重新导出它,让它留在调用者旁边)。使用 MAX_RETRIES 和 RequestTracer,而不是 _MAX_RETRIES 或 _RequestTracer。
双下划线方法(__init__、__enter__,等等)属于 Python 协议,不属于这项约定。
导入规范
使用模块的真实名称导入自己的模块——为内部代码起别名会掩盖符号的来源。当服务函数与包装它的入口点名称冲突时,使用层级后缀,而不是 import ... as。仅在两个第三方导入确实发生名称冲突,或使用惯用缩写时(import pandas as pd),才使用别名。
共享工具应放在工具模块中。当第二个模块需要使用最初在另一个模块中编写的辅助函数时,将它移到共享位置(utils.py、helpers.py)。不要仅仅因为 module_a 先定义了一个通用辅助函数,就让 module_b 依赖 module_a。
类型提示(PEP 604 和 695)
使用现代联合语法(list[str]、dict[str, int] | None)。基本类型不要从 typing 导入 List、
Dict、Optional 或 Union。
使用 PEP 695 类型参数声明泛型——class Codec[T]:、
def first[T](items: list[T]) -> T:——而不是将 TypeVar 与 Generic 搭配使用。
命名
变量、函数、类、包和模块文件名都遵循相同规则。
写出完整单词。 优先使用 document 而不是 doc,使用 request_id 而不是 req_id,使用 configuration
而不是 cfg。省略元音和截短单词只能少敲几下,却会让后续每位读者都得猜。例外是那些比其完整形式更常用的短形式——
id、url、json、csv、http——即便如此,只要有任何疑问,就应使用完整形式。
始终分隔单词。 变量、函数和模块文件名使用 snake_case;
类使用 CamelCase。绝不要将两个单词连在一起而不加分隔:使用 parse_payload,
而不是 parsepayload;使用 RetryPolicy,而不是 Retrypolicy;使用 sync_state.py,而不是 syncstate.py。
在 CamelCase 中将缩写视为单个单词,只大写首字母:
JsonParser、ApiClient、DbConnection、HttpResponse、SqlQuery——而不是 JSONParser。
日志记录
如果项目的说明规定了日志记录规范,请遵循该规范。否则使用标准库的 logging 模块,并将值作为惰性的 %s 参数传入,而不是进行插值:
logger.warning("Vector search failed: %s", exc),而不是使用 f 字符串。
参考资料
需要了解特定主题的详细模式时,可按需加载以下资料:
- 数据结构 — Pydantic 与字典、枚举、接口、TypedDict
- 文档字符串和注释 — 写多少,以及何时值得添加章节
- 模块组织 — types.py、const.py 的放置规则
- 逻辑分组 — 按关注点对字段、常量和步骤进行分组
- 并行对称性 — 两个系统处理相同输入时的命名和结构
- URL 解析 — 使用 furl 处理所有 URL 操作
验证
编写 Python 代码后,检查参考资料中提到且最容易遗漏的两条规则:
- 字段、常量和处理步骤按关注点分组,并且在各处顺序一致
- 并行处理相同输入的系统具有相同的前缀、防护条件和数据结构