跳转至

Python 工程结构、代码规范与现代 Typing 进阶(2026版)

一句话摘要:截至 2026-09-05,现代 Python 工程体系已全面确立以 src/ 布局、pyproject.toml(PEP 621/735)及 uv + ruff 工具链为核心的工业标准;Python 3.14 原生落地 PEP 649 延迟注解求值与 annotationlib,彻底淘汰了历史补丁 from __future__ import annotations;类型系统形成了以 PEP 695 泛型语法、Protocol 结构子类型、TypeIs(PEP 742)、TypedDict 为核心的现代静态表达力体系,为后续开发 LLM Agent 的结构化通信与工程基底奠定稳固基础。 调研日期:2026-09-05 环境基准:Windows 11 / Python 3.14.4 / uv 0.9.21 / ruff 0.16.6 / VS Code + Pylance


一、核心结论(面向路线制定与教学落地)

  1. 统一采用 src/ 布局与 uv init --package:PyPA 官方与现代工程界强推 src/ 布局 [1][2]。它通过物理目录隔离,防止本地未安装的源码被 sys.path[0] 隐式加载,确保单元测试始终运行在实际打包构建的隔离产物上。
  2. 构建与依赖元数据标准化(PEP 621 + PEP 735):全面抛弃 setup.pyrequirements.txt。运行时依赖归入 [project.dependencies];开发与测试依赖使用标准 [dependency-groups] 表(uv add --devuv add --group dev)[3][4],杜绝非标准的专有字段。
  3. 单文件脚本与工具执行现代化(PEP 723 + uvx):单文件实验与独立脚本通过 # /// script 内联元数据声明依赖 [5],使用 uv run script.py 自动沙箱运行;全局 CLI 工具统一使用 uvx <tool>(等价 uv tool run),彻底告别全局 pip 污染 [3]。
  4. 代码质量链条极速化(Ruff + prek / pre-commit)ruff 一站式替代 Flake8、Black、isort、pyupgrade 等十余个传统工具 [6]。格式化默认采用 88 字符宽(Black 传承),Linter 推荐规则集为 E, F, W, I, UP, B, SIM, RUF;Git 钩子首选 Rust 重写的快速替代品 prek [7][8]。
  5. 2026 年类型检查器选型格局:VS Code 日常开发基准推荐 Pylance(基于 Pyright 核心) [9],提供零配置 LSP、完整 PEP 695 泛型与 PEP 649 支持;Astral 的 ty(原 Red-Knot)与 Meta 的 pyrefly 正在 Rust 领域高速推进,但当前教学与工业首选仍是 Pyright / mypy [10][11]。
  6. Python 3.14 彻底终结 from __future__ import annotations:Python 3.14 实施 PEP 649 延迟求值机制与 annotationlib 标准库 [12][13]。类型注解在运行时以描述符函数按需求值,不再强制字符串化,前向引用直接书写即可,无需再在文件顶部添加 __future__ 声明 [14]。
  7. 类型系统以现代简写与内置泛型为唯一规范:严禁教学旧式 typing.Listtyping.Dicttyping.Optionaltyping.Union;一律使用内置泛型 list[T]dict[K, V] 与管道联合语法 T | None(PEP 604)[15]。标准容器抽象严格使用 collections.abcSequenceMappingIterable)[16]。
  8. 强化结构化子类型(Protocol)与类型收窄(TypeIs:面向 LLM Agent 的模块解耦,优先使用 Protocol(鸭子类型的静态形式)代替重量级 ABC 继承 [17];类型收窄函数从 Python 3.13 起全面采用 TypeIs(PEP 742)替代语义不精确的 TypeGuard [18]。
  9. 配置与密钥遵循 12-Factor 原则:敏感配置(API 密钥、数据库凭证)一律存入 .env 并纳入 .gitignore;工程配置读取推荐基于 Python 3.11+ 内置 tomllib 读取结构化配置,或采用 pydantic-settings 获得强类型环境变量校验与类型转换 [19]。
  10. LLM Agent 工程质量核心原则:严格执行“纯计算与 I/O 副作用隔离”、“卫语句(Guard Clauses)扁平化控制流”、“不可变数据传输(frozen dataclass)”与“结构化日志替代 print”,将软件工程防护边界筑牢在 Prompt 组装与外部 API 调用之前 [20]。

二、详细发现

2.1 项目结构与打包体系(Packaging & Project Structure)

2.1.1 src 布局 vs 扁平布局(Flat Layout)

Python Packaging User Guide(PyPA 官方指南)与现代最佳实践明确推荐使用 src 布局 [1][2]。 - 扁平布局(Flat Layout)缺陷:项目根目录下直接放置包目录(如 ./my_pkg/)。当开发者在根目录运行 pythonpytest 时,Python 默认将当前工作目录(cwd)作为 sys.path[0]。此时导入的是未经构建安装的源码目录,容易掩盖漏打包文件(pyproject.tomlfind_packages 遗漏)或未编译扩展的严重构建缺陷。 - src 布局优势:将源码放置于 src/my_pkg/。在根目录运行测试时,除非执行了可编辑安装(uv pip install -e .uv sync),否则 import my_pkg 会直接报错。这强制所有测试与调用都针对“真正构建安装后的包环境”,杜绝了“在开发机器上跑通却在用户机器上缺少模块”的隐患 [1][21]。

2.1.2 uv init 命令族行为辨析

截至 2026-09,uv(v0.9.x+)的初始化命令选项含义如下 [3]: - uv init --package:生成标准的 Python Package 项目,默认采用 src/ 布局(生成 src/pkg_name/__init__.pysrc/pkg_name/py.typed),并在 pyproject.toml 中配置 [build-system],推荐库开发和标准工程使用。 - uv init --app:生成针对独立应用程序/服务的工程,结构更轻量,配置侧重于运行时部署与服务启动入口。 - uv init --lib:生成库工程,预置构建后端元数据与发布配置。 - uv init --script <file.py>:初始化单个包含 PEP 723 内联依赖元数据的脚本文件。

2.1.3 模块入口与导入机制

  • __init__.py:标识目录为 Python 包。在现代工程中应保持轻量,仅用于导出公共 API(配合 __all__)与定义包级别文档,避免在其中执行高开销的 I/O 或初始化逻辑。
  • __main__.py:定义模块作为可执行程序被调用时的入口。当运行 python -m my_pkg 时,Python 会自动执行 my_pkg/__main__.py
  • 相对导入 vs 绝对导入
  • 包内内部模块间:允许使用显式相对导入(如 from .config import settingsfrom ..utils.helpers import clean_text),重构包名时更灵活。
  • 跨包或顶级调用:必须使用绝对导入(如 from my_pkg.core.engine import Agent),路径语义清晰,排查追溯明确。
  • 严禁使用隐式相对导入(Python 3 早已禁止)。

2.1.4 tests/ 目录组织与入口点配置

  • tests/ 放置位置:与 src/ 平级,放置于项目根目录下。测试代码不属于发布给最终用户的运行时包内容,不应放入 src/my_pkg/ 内部 [2]。
  • [project.scripts] 入口点:在 pyproject.toml 中定义 CLI 命令行工具映射,如:
    [project.scripts]
    agent-cli = "my_pkg.cli:main"
    
    安装后系统将自动生成可执行命令 agent-cli 并调用 my_pkg/cli.py 中的 main() 函数 [2]。

2.1.5 构建、发布与锁文件

  • uv build:根据 pyproject.toml 中的 [build-system]dist/ 目录下生成标准的 .tar.gz(sdist)与 .whl(wheel)二进制包 [3]。
  • uv publish:将构建好的产物安全上传至 PyPI 或私有仓库。
  • uvx <tool>:等同于 uv tool run <tool>,在完全隔离的临时虚拟环境中下载并运行指定 CLI 工具(如 uvx ruff check .uvx pytest),不污染当前工程环境 [3]。
  • uv.lock:由 uv 自动生成和管理的跨平台确定性锁文件(包含全平台依赖解析的哈希与精确版本号)。必须纳入 Git 版本控制,确保团队所有成员和 CI/CD 环境的一致性 [4]。

2.1.6 依赖分组(PEP 735)与单文件脚本(PEP 723)

  • PEP 735 Dependency Groups:2024–2025 年正式标准化的依赖分组规范。在 pyproject.toml 中使用标准的 [dependency-groups] 表:
    [dependency-groups]
    dev = ["pytest>=8.0", "ruff>=0.16.0", "pyright>=1.1.390"]
    docs = ["sphinx>=8.0", "furo>=2024.8.6"]
    
    执行 uv add --dev <pkg>uv add --group dev <pkg> 会自动写入该表 [3][4]。
  • PEP 723 Inline Script Metadata:单文件脚本依赖内联声明。无需单独建工程或写 requirements.txt
    # /// script
    # requires-python = ">=3.14"
    # dependencies = [
    #     "httpx>=0.28.0",
    #     "rich>=13.9.0",
    # ]
    # ///
    import httpx
    from rich import print
    response = httpx.get("https://api.github.com")
    print(response.json())
    
    运行 uv run script.py 时,uv 会自动解析头部注释、瞬间创建沙箱缓存环境并执行 [5]。
  • uv Workspaces(工作区):支持在单代码库(Monorepo)中通过根目录 pyproject.toml[tool.uv.workspace] 统一管理多个子包,共享单个 uv.lock,适合中大型微服务或多组件项目 [3]。

2.2 命名与代码规范(Naming, Style & Ruff)

2.2.1 PEP 8 核心命名规则与下划线约定

  • 包名与模块名:全部小写,尽量短小,可用下划线增强可读性(如 agent_coretoken_counter)。
  • 类名:大驼峰法 PascalCase(如 MessageBufferBaseAgent)。
  • 函数与方法名:小写加下划线 snake_case(如 execute_tool()format_prompt())。
  • 变量与属性名:snake_case(如 user_queryretry_count)。
  • 常量:全大写加下划线 UPPER_CASE(如 MAX_CONTEXT_TOKENSDEFAULT_TIMEOUT)。
  • 下划线约定
  • _single_leading(如 _internal_cache):弱内部使用指示(from M import * 不会导出,属于保护属性惯例)。
  • __double_leading(如 __private_attr):触发 Python 名字修饰(Name Mangling,自动重命名为 _ClassName__private_attr),用于防止子类属性冲突,非必要不滥用
  • trailing_(如 class_type_):用于避免与 Python 关键字或内置函数命名冲突。
  • __dunder__(如 __init____repr__):语言保留的特殊方法/属性,禁止自行发明双下划线名称。

2.2.2 Docstring 规范对比(Google vs NumPy vs PEP 257)

  • PEP 257:定义了 Docstring 的基本放置和格式要求(单行/多行,第一行简要说明,空一行后详细说明),但未对参数、返回值等结构做具体约定。
  • NumPy Style:使用下划线分段(如 Parameters ----------),较为冗长,广泛应用于数据科学与科学计算库(NumPy, SciPy)。
  • Google Style(强烈推荐):采用轻量级缩进与冒号声明(Args:, Returns:, Raises:),排版紧凑、易读性极高,已被 VS Code、Sphinx、MkDocs 及主流 LLM Agent 库(LangChain, LlamaIndex)广泛采用为首选标准 [22]。
    def call_llm(prompt: str, temperature: float = 0.7) -> str:
        """向大模型发送 Prompt 并获取生成结果.
    
        Args:
            prompt: 经过格式化的输入提示词.
            temperature: 采样温度, 范围 [0.0, 2.0].
    
        Returns:
            模型生成的文本字符串.
    
        Raises:
            LLMAPIError: 当上游 API 调用失败或超时时抛出.
        """
    

2.2.3 __all__ 的作用与场景

__all__ 显式定义模块的公共接口列表: 1. 控制 from module import * 导入的符号范围(防止泄漏内部辅助函数或临时 import 的第三方包)。 2. 作为代码阅读者和静态分析工具判定“公共 API”的明确声明。 3. 在包的 __init__.py 中收敛并重导出子模块 API。

2.2.4 Ruff 推荐规则集与解析

Ruff(基于 Rust)以百倍于传统工具的速度重塑了 Python 代码检查与格式化 [6]: - E / W (pycodestyle):PEP 8 代码风格错误与警告。 - F (Pyflakes):语法错误、未定义变量、未使用的 import 等致命逻辑问题。 - I (isort):Import 导入语句自动分类与排序。 - UP (pyupgrade):自动将旧语法升级为目标 Python 版本的现代语法(如将 typing.List 转为 list)。 - B (flake8-bugbear):检测常见设计陷阱与 Bug(如可变默认参数 def f(x=[]))。 - N (pep8-naming):命名规范检测(如类名必须 PascalCase)。 - D (pydocstyle):Docstring 完整度检测(初学者建议初期不设为强制,避免疲劳)。 - SIM (flake8-simplify):代码简化建议(如将冗长 if 合并、使用 context manager)。 - RUF (Ruff 专有规则):Ruff 专属的高价值检测规则。 - ANN (flake8-annotations):强制要求全函数类型注解(初中级项目建议按需开启,全量开启成本较高)。

初中级项目推荐组合select = ["E", "F", "W", "I", "UP", "B", "SIM", "RUF"],兼顾代码质量与开发体验 [6]。

2.2.5 格式化、Git 钩子与 EditorConfig

  • ruff format vs Blackruff format 是 Black 格式化器的 99.9% 兼容 Rust 实现,执行速度提升 30–100 倍。可完全移除 black 依赖,直接使用 ruff format .
  • 88 字符行宽的来由:Black 创始人 Łukasz Langa 提出。传统 PEP 8 的 79 字符源于古老终端限制,黑盒测试发现 88 字符(80 字符加上 10% 容差)能在双分屏并排查看代码时保持最佳视觉密度与换行率平衡。
  • pre-commit 与 prekpre-commit 是长期以来的 Git 钩子管理工具;2025–2026 年社区涌现出基于 Rust 开发的快速替代品 prek(j178 开发),它完全兼容 .pre-commit-config.yaml 配置,免除 Python 运行时依赖,安装与执行耗时降低数十倍 [7][8]。
  • .editorconfig:跨编辑器通用配置文件,统一缩进(indent_size = 4)、换行符(end_of_line = lf)与编码(charset = utf-8),保证团队在 VS Code、PyCharm、Cursor 间格式一致。

2.3 类型检查器 2026 年格局与选型(Type Checkers in 2026)

类型检查器 维护方 / 语言 成熟度 速度 VS Code 集成 对 Python 3.14 (PEP 649/695) 支持度 推荐场景
Pyright / Pylance Microsoft / TypeScript 极高(工业首选) 快(毫秒级增量) 官方内置(零配置开箱即用) 完美支持 PEP 695 / PEP 649 [9] 学生与开发首选
mypy Python 官方社区 / Python+C 极高(标准基准) 中等(大项目较慢) 需安装 Mypy 扩展 良好支持(通过插件扩展) [15] CI/CD 终审与遗留代码
ty (原 Red-Knot) Astral / Rust 快速演进中(Beta) 极快(Rust 增量 Salsa) 官方 LSP 扩展测试中 紧跟 3.14 最新特性,原生深度绑定 Ruff [10] 2026 尝鲜体验与前沿探索
pyrefly Meta / Rust 演进中(开源推进) 极快(Rust 架构) LSP 模式 针对超大型代码库优化 [11] 巨型 Monorepo 探索
  • Pylance 与 Pyright 的关系:Pyright 是微软开源的核心静态类型分析引擎;Pylance 是微软基于 Pyright 开发的 VS Code 闭源插件,额外集成了自动导入补全、语义高亮、代码索引与重构等 IDE 高级体验 [9]。
  • 学生最小配置推荐:直接在 pyproject.toml 中配置 [tool.pyright][tool.mypy]
    [tool.pyright]
    typeCheckingMode = "standard"
    pythonVersion = "3.14"
    include = ["src", "tests"]
    

2.4 Typing 现代进阶全解(Modern Python Typing)

2.4.1 PEP 649 延迟求值与 from __future__ import annotations 的落幕

  • 历史痛点(PEP 563)from __future__ import annotations 简单粗暴地将所有注解在编译期转化为字符串,解决了前向引用问题,但破坏了 Pydantic、FastAPI、dataclasses 等在运行时需要真实类型对象的库 [14]。
  • Python 3.14 解决方案(PEP 649 与 PEP 749):引入原生描述符延迟求值机制与 annotationlib 标准库 [12][13]。类型注解在模块加载时不立即求值,而是在首次通过 annotationlib.get_annotations(obj) 访问时按需计算。
  • 结论在 Python 3.14+ 中,无需再写 from __future__ import annotations,直接书写类型即可,循环与前向引用天然支持 [14]。

2.4.2 现代类型系统核心特性速查与范例

  1. PEP 695 泛型声明语法(Python 3.12+)
  2. 抛弃繁琐的 TypeVar 声明,使用全新方括号语法定义泛型函数、类与类型别名:
    type StringMap[T] = dict[str, T]  # PEP 695 类型别名
    
    def first_item[T](items: list[T]) -> T:
        return items[0]
    
  3. Protocol(结构化子类型 / 鸭子类型)
  4. 定义对象必须具备的方法与属性,不要求显式继承,解耦上层逻辑与具体实现:
    from typing import Protocol
    
    class LLMProvider(Protocol):
        def generate(self, prompt: str) -> str: ...
    
  5. TypedDict + NotRequired / ReadOnly(PEP 705)
  6. 为字典结构提供字段级静态类型检查,支持可选键与只读属性(非常适合 LLM JSON Schema 与 Tool Call 参数):
    from typing import TypedDict, NotRequired, ReadOnly
    
    class AgentMessage(TypedDict):
        role: ReadOnly[str]
        content: str
        metadata: NotRequired[dict[str, str]]
    
  7. TypeIs vs TypeGuard(PEP 742)
  8. TypeGuard(3.10)只在 True 分支收窄,且无法在 False 分支做排除;TypeIs(3.13+)实现双向精确类型收窄,严格保持子类型关系 [18]:
    from typing import TypeIs
    
    def is_string_list(val: list[object]) -> TypeIs[list[str]]:
        return all(isinstance(x, str) for x in val)
    
  9. ParamSpecCallable(高阶函数与装饰器)
  10. 保持被装饰函数的参数签名与返回值完全透传:
    from collections.abc import Callable
    from typing import ParamSpec, TypeVar
    
    P = ParamSpec("P")
    R = TypeVar("R")
    
    def log_call(fn: Callable[P, R]) -> Callable[P, R]:
        def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
            print(f"Calling {fn.__name__}")
            return fn(*args, **kwargs)
        return wrapper
    
  11. collections.abc vs typing 归位
  12. 集合与抽象容器:一律从 collections.abc 导入(Iterable, Iterator, Sequence, Mapping, Callable)。
  13. 类型修饰与特殊形式:从 typing 导入(Protocol, TypedDict, Literal, Final, Self, Annotated, TypeIs, cast, TYPE_CHECKING)。
  14. typing.python.org 官方新门户
  15. Python Typing Community 在 2024–2025 年上线的专门类型系统文档门户,整合了类型系统规范(Type System Reference)、最佳实践指南与各 PEP 演进历史,是学习现代 Typing 的第一手官方权威站点 [16]。

2.5 配置与密钥管理(Config & Secrets)

  1. 环境与密钥安全隔离
  2. 生产环境与开发环境配置通过环境变量隔离(遵循 12-Factor 原则)。
  3. 本地密钥存放于根目录 .env,必须写入 .gitignore 防止意外提交泄露。
  4. 仓库内提供 .env.example(仅保留键名与说明,不含真实凭证),作为模板供团队成员拷贝。
  5. 读取方式选型
  6. 基础/单文件场景:使用 Python 3.11+ 内置 tomllib 读取结构化配置文件(只读安全),或配合 python-dotenv 读取环境变量:
    import tomllib
    from pathlib import Path
    
    with Path("config.toml").open("rb") as f:
        config = tomllib.load(f)
    
  7. 工程级/LLM 应用(强烈推荐):使用 pydantic-settings,提供强类型注解、默认值、嵌套模型与环境校验 [19]:
    from pydantic_settings import BaseSettings, SettingsConfigDict
    
    class Settings(BaseSettings):
        model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
        openai_api_key: str
        temperature: float = 0.7
        max_retries: int = 3
    
    settings = Settings()
    
  8. Windows 环境变量设置方式
  9. PowerShell 临时设置:$env:OPENAI_API_KEY="sk-xxx"
  10. CMD 临时设置:set OPENAI_API_KEY=sk-xxx
  11. 系统永久生效:使用 Windows “系统属性 -> 环境变量” GUI,或命令行 setx OPENAI_API_KEY "sk-xxx"

2.6 版本管理与发布(Versioning & Releases)

  1. 版本命名规范
  2. 语义化版本(SemVer: MAJOR.MINOR.PATCH:库工程与大多数应用首选(如 1.2.0),主版本号升级代表破坏性变更(Breaking Change),次版本号代表向后兼容的功能新增,补丁号代表向后兼容的缺陷修复。
  3. 日历化版本(CalVer: YYYY.MM.MICRO:高频迭代或强时间依赖工具首选(如 pip 25.1ruff 2026.1)。
  4. 变更日志规范(Keep a Changelog)
  5. 维护根目录 CHANGELOG.md,按版本分组,清晰列出:Added(新增)、Changed(变更)、Deprecated(弃用)、Removed(移除)、Fixed(修复)、Security(安全)。
  6. 版本号获取机制
  7. 推荐使用标准库读取安装后的包版本号:
    from importlib.metadata import version
    __version__ = version("my_pkg")
    
  8. 避免在 __init__.py 中硬编码字符串;在大型 CI/CD 中可选用 hatch-vcsuv-dynamic-versioning 从 Git Tag 动态推导版本。

2.7 代码质量与工程设计清单(Code Quality Checklist)

  1. 函数规模与参数限制
  2. 函数长度经验控制在 30–50 行以内,坚持单一职责原则(Single Responsibility)。
  3. 参数数量建议 <= 3–4 个;超过 4 个参数时,应重构为 Pydantic 数据模型或 dataclass
  4. 卫语句(Guard Clauses)与扁平结构
  5. 遵循“扁平优于嵌套”(The Zen of Python)。遇到不合法参数或边界条件立即 returnraise,减少嵌套缩进:
    # 推荐:卫语句快速失败
    def process_message(msg: Message | None) -> None:
        if msg is None:
            return
        if not msg.content:
            raise ValueError("Message content is empty")
        send_to_llm(msg)
    
  6. “计算与 I/O 分离”与纯函数
  7. 核心业务逻辑(数据清洗、Prompt 模板格式化、状态计算)编写为纯函数(无副作用、相同输入恒定输出、易于单元测试)。
  8. 网络请求、文件读写、数据库操作等有副作用的 I/O 集中在系统最外层。
  9. 数据不可变性(Immutability)
  10. 传输对象优先采用不可变数据类 @dataclass(frozen=True),防止在多模块或异步并发流转中发生不可预期的隐式修改。
  11. 结构化日志替代 print
  12. 生产代码全面禁止裸写 print()。统一使用标准库 logging 或现代结构化日志库(如 structlog / loguru),合理分配日志级别(DEBUG, INFO, WARNING, ERROR)。
  13. piglei《Python 工匠》核心封装建议
  14. 知名 Python 技术著作《Python 工匠》(GitHub 开源地址:https://github.com/piglei/one-python-craftsman [20])针对工程封装提出:
    • 隐藏实现细节:模块间只暴露最小必要的公开 API,充分利用私有属性与 __all__
    • 面向容器接口而非具体类型编程:接收参数时声明 Sequence[str] 而不是 list[str],提高函数通用性。
    • 善用 Python 协议:通过实现 __len____iter____enter__ 等魔法方法,使自定义对象无缝融入 Python 原生生态。

三、推荐学生项目模板(Student Project Template)

3.1 标准目录树结构

my-agent-project/
├── .editorconfig              # 跨编辑器格式规范
├── .env.example               # 环境变量模板(不含敏感值)
├── .gitignore                 # Git 忽略文件(包含 .env, .venv, dist/ 等)
├── .python-version            # 固定 Python 版本 (3.14.4)
├── CHANGELOG.md               # 版本变更记录
├── LICENSE                    # 开源协议
├── README.md                  # 项目介绍与快速开始
├── pyproject.toml             # 项目元数据、依赖与工具配置总入口
├── uv.lock                    # uv 全平台确定性锁文件
├── src/                       # 核心源码目录(src 布局)
│   └── agent_core/            # 核心业务包
│       ├── __init__.py        # 包导出与 __all__
│       ├── __main__.py        # 命令行直接运行入口 (python -m agent_core)
│       ├── py.typed           # 声明该包提供类型注解 (PEP 561)
│       ├── config.py          # pydantic-settings 配置模型
│       ├── models.py          # 数据结构定义 (TypedDict / dataclass)
│       └── runner.py          # 核心执行引擎
└── tests/                     # 自动化测试目录
    ├── __init__.py
    ├── conftest.py            # pytest 共享 fixture
    └── test_runner.py         # 单元测试用例

3.2 完整 pyproject.toml 模板

[project]
name = "agent-core"
version = "0.1.0"
description = "High school advanced Python engineering template for LLM Agents"
readme = "README.md"
requires-python = ">=3.14"
authors = [
    { name = "Student Developer", email = "student@example.com" }
]
dependencies = [
    "httpx>=0.28.0",
    "pydantic>=2.10.0",
    "pydantic-settings>=2.8.0",
    "structlog>=25.1.0",
]

[project.scripts]
agent-runner = "agent_core.runner:main"

[dependency-groups]
dev = [
    "pytest>=8.3.0",
    "pytest-asyncio>=0.25.0",
    "ruff>=0.16.0",
    "pyright>=1.1.390",
]

[build-system]
requires = ["uv_build>=0.12.0"]
build-backend = "uv_build"

# ----------------- Ruff 代码质量与格式化配置 -----------------
[tool.ruff]
line-length = 88
target-version = "py314"
src = ["src", "tests"]

[tool.ruff.lint]
select = [
    "E",    # pycodestyle errors
    "W",    # pycodestyle warnings
    "F",    # Pyflakes
    "I",    # isort
    "UP",   # pyupgrade (现代 Python 语法)
    "B",    # flake8-bugbear
    "SIM",  # flake8-simplify
    "RUF",  # Ruff-specific rules
]
ignore = [
    "E501", # 行宽由 formatter 处理,linter 不报警
]

[tool.ruff.lint.isort]
known-first-party = ["agent_core"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"

# ----------------- Pyright 静态类型检查配置 -----------------
[tool.pyright]
include = ["src", "tests"]
pythonVersion = "3.14"
typeCheckingMode = "standard"
strictListInference = true
strictDictionaryInference = true

# ----------------- Pytest 测试配置 -----------------
[tool.pytest.ini_options]
minversion = "8.0"
testpaths = ["tests"]
pythonpath = ["src"]
asyncio_mode = "auto"

3.3 关键配套配置

  • .gitignore 关键项:
    .venv/
    __pycache__/
    *.py[cod]
    .env
    dist/
    .pytest_cache/
    .ruff_cache/
    
  • .editorconfig 关键项:
    root = true
    
    [*]
    charset = utf-8
    end_of_line = lf
    insert_final_newline = true
    trim_trailing_whitespace = true
    indent_style = space
    indent_size = 4
    

四、现代 Typing 特性速查表

特性 / 构造 一句话说明 现代最小示例 (Python 3.14) 推荐掌握档位
T | None 替代旧式 Optional[T],表达可空值 name: str | None = None A (必修)
内置泛型容器 替代 typing.List/Dict,直接使用内置类型 items: list[str] = [] A (必修)
Literal 限定取值为一组固定字面量常量之一 mode: Literal["sync", "async"] A (必修)
collections.abc 统一抽象容器接口(Sequence/Mapping/Iterable) def run(tasks: Sequence[str]): ... A (必修)
PEP 695 泛型 方括号原生泛型语法,无需额外 TypeVar def first[T](arr: list[T]) -> T: ... A (必修)
Protocol 静态鸭子类型(结构化子类型),实现接口解耦 class Tool(Protocol): def run(self): ... A (必修)
TypedDict 为特定键值结构字典提供强类型声明(含 ReadOnly) class User(TypedDict): name: str; age: int A (必修)
Self 链式调用/类方法中指代当前实例的具体类型 def set_name(self, n: str) -> Self: ... B (进阶)
Final 声明不可重新赋值的常量或禁止被重写的方法 MAX_SIZE: Final[int] = 100 B (进阶)
Annotated 为类型附加元数据(常用于 Pydantic 校验与字段说明) Age = Annotated[int, Field(ge=0)] B (进阶)
TypeIs (PEP 742) 替代 TypeGuard,实现 if/else 双向精准类型收窄 def is_int(x: object) -> TypeIs[int]: ... B (进阶)
Callable 声明可调用函数对象的参数签名与返回值 cb: Callable[[int, str], bool] B (进阶)
TYPE_CHECKING 仅在类型检查期生效的守卫常量,杜绝运行时循环导入 if TYPE_CHECKING: from .heavy import BigCls B (进阶)
overload 声明函数的多种重载签名(配合一个具体实现) @overload / def get(x: int) -> str: ... B (进阶)
Never / NoReturn Never 用于穷尽性模式匹配;NoReturn 标注不退出的函数 def abort() -> NoReturn: sys.exit(1) B (进阶)
typing.cast 运行时无操作,强制告知类型检查器目标类型 x = cast(str, unknown_obj) C (知晓)
ParamSpec 精确保留高阶函数/装饰器传参签名的参数规范变量 P = ParamSpec("P") C (知晓)

注:档位说明 —— A (必修):日常开发必须熟练运用的核心语法;B (进阶):编写健壮框架与复杂逻辑必备;C (知晓):底层装饰器开发或特殊边界处理时使用。


五、来源列表

  1. Python Packaging User Guide - src layout vs flat layout: https://packaging.python.org/en/latest/discussions/src-layout-vs-flat-layout/
  2. pyOpenSci Python Package Guide - Package Structure & Layout: https://www.pyopensci.org/python-package-guide/package-structure-code/python-package-structure.html
  3. Astral uv Documentation - CLI Commands & Project Management: https://docs.astral.sh/uv/reference/cli/
  4. Astral uv Documentation - Managing Dependencies & PEP 735: https://docs.astral.sh/uv/concepts/projects/dependencies/
  5. PEP 723 – Inline script metadata: https://peps.python.org/pep-0723/
  6. Astral Ruff Documentation - The Ruff Linter & Rules: https://docs.astral.sh/ruff/
  7. GitHub Repository - j178/prek (Fast Git hook manager in Rust): https://github.com/j178/prek
  8. Astral ruff-pre-commit Official Hook Integration: https://github.com/astral-sh/ruff-pre-commit
  9. Pyright Static Type Checker Documentation: https://microsoft.github.io/pyright/
  10. Talk Python To Me Episode #506 - ty: Astral New Type Checker: https://talkpython.fm/episodes/show/506/ty-astrals-new-type-checker-formerly-red-knot
  11. Edward Li Tech Blog - Pyrefly vs ty: https://blog.edward-li.com/tech/comparing-pyrefly-vs-ty
  12. PEP 649 – Deferred Evaluation of Annotations Using Descriptors: https://peps.python.org/pep-0649/
  13. PEP 749 – Implementing PEP 649 and annotationlib: https://peps.python.org/pep-0749/
  14. Python 3.14 Release Documentation - What is New in Python 3.14: https://docs.python.org/3/whatsnew/3.14.html
  15. PEP 604 – Allow writing union types as X | Y: https://peps.python.org/pep-0604/
  16. Python Typing Community Official Portal: https://typing.python.org/
  17. Python Typing Documentation - Protocols and Structural Subtyping: https://typing.python.org/en/latest/guides/protocols.html
  18. PEP 742 – Narrowing types with TypeIs: https://peps.python.org/pep-0742/
  19. Pydantic Settings Official Documentation: https://docs.pydantic.dev/latest/concepts/pydantic_settings/
  20. GitHub Repository - piglei/one-python-craftsman (Python 工匠): https://github.com/piglei/one-python-craftsman
  21. James Bennett - Python packaging: use the src: https://www.b-list.org/weblog/2023/dec/15/python-packaging-src-layout/
  22. Google Python Style Guide - Docstrings: https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings