TheAlgorithms/Python 贡献指南详解:编写可测试、带类型标注且能通过自动化 CI 的算法实现
本文以 CONTRIBUTING.md 为核心,系统讲解 TheAlgorithms/Python 算法库对贡献者的完整要求:什么样的代码才算一个合格的“算法”、必须满足哪些命名、docstring、doctest 与类型标注规范,以及 pre-commit、ruff、mypy、pytest 等自动化工具链如何逐层拦截不合规的提交。读完本文,你可以按仓库实际执行的验证标准写出一次就能通过 CI 的 PR。
一、贡献前必须确认的三件事
仓库欢迎实现算法与数据结构的新贡献者,但要求提交者在发起 pull request 之前完整阅读贡献指南;对指南本身有疑义时,应通过 issue 明确说明(原文档中的 issue 入口、Gitter 社区链接属于外部社区页面,此处不再罗列)。
成为贡献者即表示确认以下三点:
- 代码为本人独立完成,不抄袭——任何抄袭作品不会被合并;
- 提交的工作在 PR 合并后将以 MIT License 分发,对应仓库根目录的 LICENSE.md;
- 提交内容满足(或基本满足)仓库的编码风格与标准。
关于“做什么”的边界,指南划得很清楚:
- 欢迎全新实现:同一问题的新解法、图数据结构的另一种表示、复杂度不同的算法设计;
- 不允许对已有实现的完全相同复制。提交前必须先自查该解法是否已经存在(可检索目录导航 DIRECTORY.md);
- 改进注释、补写规范测试同样是高价值贡献,与实现复杂算法同等被欢迎。
二、仓库对“算法”的操作性定义
这是贡献前最关键的判定标准。仓库认为一个算法是一或多个函数(或类),它应当:
- 接收一个或多个输入;
- 执行内部计算或数据操作;
- 返回一个或多个输出;
- 副作用最小化(如
print()、plot()、read()、write()都应避免)。
进一步地,算法应满足以下八条要求:
- 类名与函数名直观,读者一眼能看懂用途;
- 遵循 Python 命名约定,变量名直觉化;
- 灵活地接受不同的输入值;
- 输入参数与返回值带 Python 类型标注(type hints);
- 对错误输入抛出 Python 异常(如
ValueError); - 带 docstring,清晰解释算法或给出来源资料链接;
- 包含覆盖合法输入与错误输入的 doctest;
- 返回计算结果,而不是打印或绘图。
指南还特别强调:仓库中的算法不应是现成 Python 第三方库的 how-to 示例,而应自己完成把输入转换为输出的计算;可以使用第三方库的数据类型、类或函数,但每个算法必须提供独特价值。这条规则解释了为什么仓库的 maths/、sorts/ 等目录下的实现几乎都是纯计算逻辑。
三、本地自动化第一道关卡:pre-commit
指南建议使用 pre-commit 钩子在每次 commit 时自动按仓库风格修正代码:
python3 -m pip install pre-commit # 仅首次需要
pre-commit install
安装后插件会在每次 commit 时运行;如发现问题,修复后再提交。也可以手动对全部文件强制执行:
pre-commit run --all-files --show-diff-on-failure
当前仓库的 .pre-commit-config.yaml 给出了这套钩子的完整清单,可作为“提交前会被检查什么”的权威参照:
- pre-commit-hooks:
check-toml、check-yaml、end-of-file-fixer(仅 Python 文件)、trailing-whitespace、check-executables-have-shebangs、requirements-txt-fixer; - auto-walrus:自动简化海象运算符写法;
- astral-sh/ruff-pre-commit:
ruff-check与ruff-format两个钩子,即 lint 与格式化; - codespell:拼写检查(词表与跳过规则配置在 pyproject.toml 的
[tool.codespell]段); - pyproject-fmt 与 validate-pyproject:保持
pyproject.toml格式与合法性; - local 钩子
validate-filenames:直接执行 scripts/validate_filenames.py; - prettier:对 TOML/YAML 文件做格式化。
其中 validate-filenames 钩子把指南中“文件名严格使用 snake_case”的要求变成了硬校验。从 scripts/validate_filenames.py 的源码看,它会遍历所有合法文件路径,逐一检测大写字母、空格、连字符以及未放入目录的散文件,任一违规即以非零退出码终止钩子。这正是指南第 7 节“strictly use snake_case”的机器实现。
四、编码风格:命名、版本、格式化与 Lint
4.1 Python 版本基线
指南正文写的是“请使用 Python 3.13+”(例如 print "Hello" 已不可用,必须 print("Hello"))。需要注意当前仓库的实际情况已经前进:pyproject.toml 声明 requires-python = ">=3.14",ruff 的 target-version = "py314"、mypy 的 python_version = "3.14" 均以 3.14 为准;CI 工作流 .github/workflows/build.yml 中 setup-python 安装的也是 3.14。也就是说,文档写 3.13+ 是下限要求,而当前仓库代码与 CI 的实际运行基线是 Python 3.14,新提交建议直接按 3.14 的语法能力来写。
4.2 命名规范
- 高度重视函数、类、变量命名,用描述性名称替代冗余注释;
- 避免单字母变量名,除非其生命周期只有几行;
- 缩写要展开:
gcd()难以理解,而greatest_common_divisor()不会; - 遵循 Python 命名约定:变量与函数用小写下划线(
lower_case),常量全大写(UPPERCASE),类名 CamelCase。
这一条与 ruff 的 N(pep8-naming)规则组直接对应:pyproject.toml 的 lint.select 启用了 N、PL 等命名相关规则组。
4.3 字符串格式化、black 与 ruff
- 鼓励在能提升可读性时使用 f-string;
- 建议在提交前用 PSF 的 black 格式化(指南说明其尚非强制,但能自动对齐 PEP 8 的多数要求):
python3 -m pip install black # 仅首次需要
black .
- 所有提交必须通过
ruff检查才会被接受,因此务必在本地先跑:
python3 -m pip install ruff # 仅首次需要
ruff check
当前仓库的 ruff 配置远比默认严格:pyproject.toml 中 lint.select 启用了约 40 个规则组,涵盖 F(Pyflakes)、E/W(pycodestyle)、B(bugbear)、S(bandit 安全)、SIM(简化)、UP(pyupgrade)、N(命名)、I(isort)等,同时按注释显式保留了一批“暂不修复”的规则(如 S101 允许 assert,与 doctest 风格兼容)。此外还有若干量化上限,例如 McCabe 圈复杂度上限被放宽到 17(lint.mccabe.max-complexity = 17),pylint.max-branches = 20。另有独立工作流 .github/workflows/ruff.yml 在 CI 中执行 ruff,与指南“All submissions will need to pass the test ruff .”相互印证。
4.4 docstring 与注释的合格线
- 原创代码提交必须带 docstring 或注释说明工作内容;
- 若参考了 Wikipedia 等来源,请把 URL 写进 docstring 或注释帮助读者溯源(.github/pull_request_template.md 的清单也要求“所有新算法至少包含一个指向 Wikipedia 或类似解释页面的 URL”);
- 过细的注释会被打回,例如:
x = x + 2 # increased by 2
这种注释过于琐碎;注释应当有解释价值,且位置(行上/行尾/行下)在同一代码块内保持一致。
指南给出的 docstring 正例(注意缩进):
def sum_ab(a, b):
"""
Return the sum of two integers a and b.
"""
return a + b
4.5 doctest:被 pytest 自动采集的文档测试
指南强烈建议所有函数都写 doctest,并给出完整示例:
def sum_ab(a, b):
"""
Return the sum of two integers a and b
>>> sum_ab(2, 2)
4
>>> sum_ab(-2, 3)
1
>>> sum_ab(4.9, 5.1)
10.0
"""
return a + b
这些 doctest 会被 pytest 作为自动化测试的一部分运行,因此提交前应在本地确认它们能被发现并通过:
python3 -m doctest -v my_submission.py
这条要求并非空话。从 pyproject.toml 的 pytest 配置看,ini_options.addopts 中全局启用了 --doctest-modules,意味着 pytest 对每个模块都会执行 doctest 收集;.github/workflows/build.yml 的测试步骤正是 uv run --with=pytest-run-parallel pytest ... --cov=. .,doctest 失败等同于 CI 失败。同时 PR 模板明确要求“All functions have doctests that pass the automated testing”。
4.6 input() 的使用约束
- 内置
input()不鼓励使用,尤其禁止input = eval(input(...))这类危险写法:
input('Enter your input:')
# Or even worse...
input = eval(input("Enter your input: "))
- 如果确需
input(),应优雅处理首尾空白,追加.strip():
starting_value = int(input("Please enter a starting value: ").strip())
4.7 类型标注与 mypy
- 建议为函数参数与返回值添加类型标注,因为自动化测试会运行 mypy:
def sum_ab(a: int, b: int) -> int:
return a + b
- 本地检查命令:
mypy --ignore-missing-imports . # 测试所有文件
mypy --ignore-missing-imports path/to/file.py # 测试单个文件
- pyproject.toml 中
[tool.mypy]将python_version固定为3.14。需要说明的现状是:当前 .pre-commit-config.yaml 中的 mypy 钩子处于注释状态,但指南仍将 mypy 列为提交前必查项,PR 模板也把“所有函数参数与返回值带类型标注”列入检查清单,因此本地手动跑 mypy 仍是实际要求。
4.8 其他风格条款
- 列表推导式与生成器优先于
lambda、map、filter、reduce,但核心是展示易读易维护的 Python; - 基础算法避免引入外部库,复杂算法才允许使用第三方库;
- 指南原文要求“需要
requirements.txt中不存在的第三方模块时,请把它加入该文件”。对照当前仓库实际状态:根目录已不存在requirements.txt,依赖改由 pyproject.toml 的dependencies声明并用 uv.lock 锁定(CI 通过uv sync --group=test安装)。因此新增依赖的正确做法是在pyproject.toml中声明并更新锁文件。
五、Issue 工作流:不指派、不占坑、用 Fixes # 自动关闭
指南对 issue 的处理方式有三条硬规则:
- 想解决某个 open issue,直接提交带修复方案的 PR 即可——本仓库不指派 issue,不要请求许可;
- 不要为“想贡献某个算法”而开 issue,应直接提 PR;
- 为保持 issue 列表精简,解决某个 issue 的 PR 必须在描述中写入关闭关键字。例如 PR 修复 issue #10,则描述中加入:
Fixes #10
GitHub 会在 PR 合并时据此自动关闭该 issue。.github/pull_request_template.md 的最后一条检查项也要求 PR 描述包含 Fixes #ISSUE-NUMBER 形式的关闭关键字,两者口径一致。
六、其余提交要求:目录、命名与自动生成文件
- 向
project_euler/目录提交代码前,必须先阅读该目录的专门指南 project_euler/README.md; - 代码文件扩展名必须是
.py,Jupyter Notebook 不归本仓库收录; - 文件名严格使用 snake_case(下划线分隔),便于脚本解析——如前所述,这一条由
validate-filenames钩子机器强制; - 尽量避免新建目录,优先把代码放入现有目录结构,并跟随目标文件夹内部已有的惯例;
- 修改/新增代码后,提交前确认代码可编译运行;修改/新增文档则要求语言简洁、无语法错误;
- 不要手动更新 README.md 或 DIRECTORY.md——两者由 GitHub Actions 定期自动生成。从源码可印证:.github/workflows/build.yml 在测试成功后执行
scripts/build_directory_md.py 2>&1 | tee DIRECTORY.md,由 scripts/build_directory_md.py 重建目录导航; - 所有提交都会被 mypy 测试,鼓励在有意义处添加类型标注;
- 指南最后强调:提交时请始终一致地遵守这些准则。
七、这些要求如何被 CI 与运维脚本实际执行
把 CONTRIBUTING.md 的条款放回仓库基础设施中,可以看到一条完整的“文档—工具—CI—运维”闭环:
- pytest 主测试流:.github/workflows/build.yml 在
ubuntu-latest上安装 Python 3.14、用 uv 同步test依赖组后,以pytest-run-parallel并行执行全仓测试(--doctest-modules由 pyproject.toml 全局开启),并附--cov覆盖率报告;同时--ignore掉若干依赖网络或重模型的例外文件(如web_programming/current_stock_price.py、machine_learning/lstm/lstm_prediction.py、project_euler/等)。这正是指南所说“提交后在 PR 页面底部观察 Actions 测试、失败时点 details 查看输出”的测试来源。 - ruff 独立工作流:.github/workflows/ruff.yml 单独保证 lint 关卡。
- PR 模板即验收清单:.github/pull_request_template.md 把“原创声明、单一 PR 只改一个算法文件、文件放入现有目录、全小写无空格无连字符的文件名、命名规范、类型标注、doctest 通过、含 Wikipedia 链接、Fixes # 关键字”全部显式列成 checkbox,与本文各节条款一一对应。
- 超量 PR 的批量清理:维护者还依据 CONTRIBUTING.md 的硬性标准编写了运维脚本。从 scripts/README.md 看,
close_pull_requests_with_require_tests.sh、close_pull_requests_with_require_type_hints.sh、close_pull_requests_with_require_descriptive_names.sh、close_pull_requests_with_failing_tests.sh等脚本用于关闭不满足“测试、类型标注、描述性命名”要求的 PR——这解释了为什么指南中 doctest 与 type hints 不是建议而是准入门槛。
八、小结:一份可直接执行的提交前检查清单
综合 CONTRIBUTING.md 全文与仓库工具链现状,一次合格的提交前应完成:
- 确认解法不与现有实现重复,且代码放在现有目录中;
- 文件名全小写 snake_case,无空格、连字符或大写(
validate-filenames会拦截); - 函数/类命名描述化,参数与返回值带类型标注,错误输入抛异常;
- 每个函数写 docstring(含来源 URL)与覆盖正误输入的 doctest,并本地跑
python3 -m doctest -v <file>验证; - 本地执行
pre-commit run --all-files --show-diff-on-failure,保证 ruff check/format、拼写与 TOML/YAML 检查全部通过; - 本地执行
ruff check与mypy --ignore-missing-imports <file>; - 若解决 issue,在 PR 描述写入
Fixes #N; - 不要改动 README.md / DIRECTORY.md,不要新建目录。
按这条清单提交,你的 PR 就能与 pyproject.toml、.pre-commit-config.yaml 和 .github/workflows/build.yml 所定义的验证体系完整对齐,一次通过自动化测试。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00