Skip to content

1. Python 项目开发规范

1. Python 项目开发概述

Python 版本

本文仅考虑 Python 3.10+ 版本,不考虑陈旧版本的 Python 项目。

Python 是一种高级编程语言,它的设计哲学强调代码的可读性和简洁的语法。Python 是一种解释型语言,其解释器可以在多种操作系统上运行,包括 Windows、macOS 和各种形式的 Unix 系统。

当需要调用机器学习等模型时,Python 是首选的开发语言。Python 也广泛适用于服务器脚本开发、Web 开发、数据分析和客户端开发等领域。

但一直以来,Python 的项目一直没有严格的规范,导致了项目的混乱。为了解决这个问题,我们制定了一套 Python 项目开发规范。

选择 Python 版本时,不要使用最新的版本。推荐使用次新的版本或前两个版本,确保第三方库得到及时维护,当前推荐的版本为 3.13.11

2. 环境配置指南

2.1 安装 Python

安装 Python 的方法如下,Windows 10+:

bash
winget install Python.Python.3.13

Ubuntu/Debian:

bash
sudo apt install python3

CentOS/RHEL:

bash
sudo yum install python3

macOS:

bash
brew install python
brew install python@3.9
brew install python@3.10
brew install python@3.11
brew install python@3.12
brew install python@3.13

3. Python 开发基本规范

3.1 核心规范

  • 包和模块的命名必须符合 PEP8 规范;
  • 项目根目录下必须有 pyproject.toml 文件,用于管理项目依赖;
  • 每个包下都必须有 __init__.py 文件,可以为空文件;
  • 包和模块的名称尽量和关键字、标准库和常见第三方库的名称不同,防止导入冲突或混淆;
  • 保持你的代码在测试覆盖之下,推荐使用 pytest
  • 谨慎地使用并发,特别是针对线程和异步;

3.2 代码风格规范

所有的代码风格遵循 PEP8,以下内容均在此基础上进行补充:

  • 统一使用 Black 风格的代码(下文提供指定格式化工具介绍);
  • Python 代码一律缩进使用 4 个空格;
  • 在项目中优先使用绝对导入,避免使用相对导入;
  • 使用 r 前缀表示正则表达式,使用 R 前缀表示原始字符串;
  • 仅作为类型使用的导入需要单独处理,下面是三种方法:
    1. 使用 from __future__ import annotations,然后将类型导入放在 if TYPE_CHECKING: 语句下面;
    2. 将类型导入放在 if TYPE_CHECKING: 语句下面,将每个类型注解使用字符串包裹;
    3. 将类型导入放在文件的最后; 通过这几种方法可以防止循环导入,并避免导入不需要的依赖。相当于 TypeScript 中的 import type { ... } from ...

3.3 异步编程规范

NOTE

线程(Threads)和异步 I/O(asyncio)是不恰当的组合,只会带来无尽的痛苦。或许我们需要更强烈的警告来反对将它们混合使用,但用户们还是会无视这些警告。 ——Guido van Rossum(Python 作者)

  • 确保没有任何愚蠢的函数阻塞异步事件循环。即禁止在异步函数(即 async 开头的函数)内使用阻塞操作,如 sleep.time()os.listdir() 和任何涉及网络、文件和系统 I/O 的内容;

  • 避免在非阻塞的函数前添加 async 标记,这样会绑架用户强制使用异步编程;

  • 避免混合使用异步和同步阻塞函数,否则将引入线程切换开销和复杂的并发控制手段;

  • 如果异步函数内调用了阻塞操作,请使用 asyncio.to_thread 进行包装,例如下面代码将 sleep() 转为线程支持:

    python
    import asyncio
    import time
    
    def blocking_function(seconds: int = 2) -> str:
        time.sleep(seconds)
        return "Completed"
    
    async def main():
      await asyncio.to_thread(blocking_function, 5)

3.4 元编程规范

对于元编程,我们需要避免使用一些危险的操作:

  • 禁止在开发功能时反射源代码和字节码,也避免使用一切反射源代码和字节码的第三方库。 反射源代码操作在编译为字节码或原生编译后无法执行,因为源代码已经不存在。同样也避免包括反射 __code__ 对象,反射 co_code 等字节码对象,操作字节码可能触发解释器层面的问题或内存泄漏等问题,如修改字节码可能出现 Segmentation Fault。 并且,反射源代码和字节码无法被 Nuitka 等工具打包为单独的可执行文件。例如 ORM 库 mayim 通过反射代码执行操作,在打包后不能工作。 建议将反射操作替换为反射类型签名(类型注解),类型签名是标准功能,可借助 typing.Annotated 扩展其反射内容。
  • 避免使用执行代码的函数,如 eval()exec(),除非用于测试环境,否则将产生各种安全问题,参见 Python Cookbook。compile() 本身也不够安全,可能产生攻击,需要避免使用。
  • 避免使用 os.system() 函数,请使用 subprocess 模块。避免信任来自用户的命令输入,避免使用 Shell 模式执行命令。

3.5 混合语言规范

特别地,对于使用 Qt 等 C/C++/Go/Java 库的 Python 项目,部分变量或函数的命名可以不遵循 PEP8,而使用对于项目的命名来提供一致性的接口。例如,对于 Qt 项目中,在 Python 下创建的槽函数、信号等的名称使用小驼峰命名。

4. Python 项目管理

4.1 编辑器配置

推荐 VS Code 插件配置,编辑 .vscode/extensions.json

json
{
  "recommendations": [
    "ms-python.python",
    "charliermarsh.ruff",
    "fill-labs.dependi",
    "EditorConfig.EditorConfig",
    "tamasfe.even-better-toml",
    "streetsidesoftware.code-spell-checker"
  ]
}

.vscode/settings.json 配置:

json
{
  "python.analysis.autoImportCompletions": true,
  "python.analysis.typeCheckingMode": "standard",
  "python.analysis.importFormat": "absolute",
  "python.testing.pytestArgs": [
    "."
  ],
  "python.testing.unittestEnabled": false,
  "python.testing.pytestEnabled": true,
  "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff",
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.fixAll": "explicit",
      "source.convertImportFormat": "never",
      "source.organizeImports.ruff": "explicit"
    }
  },
  "isort.args": [
    "--profile",
    "black"
  ],
  "files.eol": "\n",
  "cSpell.words": [
    "asyncio",
    "dotenv",
    "fastapi",
    "httpx",
    "mypy",
    "noninteractive",
    "pydantic",
    "pypi",
    "pyproject",
    "pytest",
    "sdist",
    "uvicorn",
    "venv"
  ]
}

编辑器通用配置,编辑 .editorconfig:

ini
root = true

[*]
indent_size = 2
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.py]
indent_size = 4

[*.toml]
indent_size = 4

[*.md]
trim_trailing_whitespace = false

4.2 包管理器

4.2.1 使用 uv

我们推荐使用 uv 作为唯一的包管理器。

Linux 和 macOS 使用 curl 安装:

bash
curl -LsSf https://astral.sh/uv/install.sh | sh

Windows 使用 powershell 安装:

bash
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

也可以使用 pip 安装:

bash
pip install uv

在 Windows 上 UV 默认缓存在 C 盘,这样会导致安装的包无法使用硬链接,无法使用 UV 节省空间的优势。可以将缓存目录设置到工作用的盘符上。例如配置环境变量,将缓存目录设置到 D 盘:

ini
UV_CACHE_DIR=D:\.uv-cache

UV 会严格进行依赖解析,确保每个系统上都能安装对应的 Python 包,并生成锁文件 uv.lock

有时我们需要针对某些包进行解依赖,以便不同系统上都能安装到正确的版本,例如 PyTorch:

toml
dependencies = [
    "torch==2.7.0+cu128; sys_platform != 'darwin'",
    "torch==2.7.0; sys_platform == 'darwin'",
    "torchaudio==2.7.0+cu128; sys_platform != 'darwin'",
    "torchaudio==2.7.0; sys_platform == 'darwin'",
    "torchvision==0.22.0+cu128; sys_platform != 'darwin'",
    "torchvision==0.22.0; sys_platform == 'darwin'",
]

[tool.uv.sources]
torch = [
    { index = "pytorch-cpu", marker = "sys_platform == 'darwin'" },
    { index = "pytorch-cu128", marker = "sys_platform != 'darwin'" },
]
torchaudio = [
    { index = "pytorch-cpu", marker = "sys_platform == 'darwin'" },
    { index = "pytorch-cu128", marker = "sys_platform != 'darwin'" },
]
torchvision = [
    { index = "pytorch-cpu", marker = "sys_platform == 'darwin'" },
    { index = "pytorch-cu128", marker = "sys_platform != 'darwin'" },
]

[[tool.uv.index]]
name = "pytorch-cu128"
url = "https://download.pytorch.org/whl/cu128"
explicit = true

[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true

4.2.2 初始化项目

初始化新项目:

bash
uv init

初始化一个包项目:

bash
uv init --package

只有包项目才能构建为 Wheel 进行分发。

指定 Python 版本:

bash
uv init --python 3.13

4.3 多包管理

uv 支持使用 Workspace 管理 Monorepo(即单个仓库多个包)项目。

推荐在父项目(即最外层项目)配置:

toml
[tool.uv.workspace]
members = [
    "packages/*",
]

新建文件夹 packages/,在此文件夹执行初始化命令会自动注册子包到父项目中:

bash
cd packages/
uv init child_proj --package

4.4 代码风格检查

TODO:介绍 sort-importsblackruff 等工具。

Python 代码格式化工具有很多,比如:

  • autopep8
  • flake8
  • autoflake
  • pyflakes
  • pycodestyle
  • pylint
  • isort
  • black
  • yapf
  • Ruff

mypy 类型检查:https://github.com/run-llama/llama_index/blob/main/pyproject.toml

4.5 提交前检查:pre-commit 钩子

pre-commit 是一个用于管理 Git 钩子的工具,可以在提交代码前自动运行代码格式化、代码检查等工具。

推荐使用 uv 安装 pre-commit

bash
uv add --dev pre-commit

使用 pre-commit 很简单,只需要在项目根目录下创建 .pre-commit-config.yaml 文件:

yaml
fail_fast: false

exclude: |
  (?x)^(
    .vscode/.*|
    uv.lock
  )$
repos:

- repo: <https://github.com/abravalheri/validate-pyproject>
    rev: v0.24.1
    hooks:
  - id: validate-pyproject
- repo: <https://github.com/pre-commit/pre-commit-hooks>
    rev: v6.0.0
    hooks:
  - id: check-added-large-files
  - id: check-builtin-literals
  - id: check-case-conflict
  - id: check-docstring-first
  - id: check-executables-have-shebangs
  - id: check-json
  - id: check-merge-conflict
  - id: check-toml
  - id: check-yaml
  - id: detect-private-key
  - id: end-of-file-fixer
  - id: mixed-line-ending
        args: [--fix=lf]
  - id: trailing-whitespace
- repo: <https://github.com/astral-sh/ruff-pre-commit>
    rev: v0.14.5
    hooks:
  - id: ruff
        args: [--fix]
  - id: ruff-format

在项目根目录下执行:

bash
pre-commit install

现在,每次提交代码时,pre-commit 将会自动运行配置的钩子。 注意 exclude 字段中需要添加项目中所有需要忽略的文件,使用正则表达式匹配。 现在可以手动运行测试:

bash
pre-commit run --all-files

自动更新规范版本:

bash
pre-commit autoupdate

4.6 类型检查

TODO:介绍 Pylance 和 Mypy.

5. Docker 镜像选型和构建

拉取当前推荐的版本:

bash
docker pull python:3.13.11

精简版本:

bash
docker pull python:3.13.11-alpine3.23

构建使用的版本:

bash
docker pull python:3.13.11-trixie

发布版本:

bash
docker pull python:3.13.11-slim-trixie

Dockerfile 示例

下面提供一个 Dockerfile 样例,这是一个基于 Sanic 的 Web 项目。此样例使用了多阶段构建,第一阶段使用构建版本的镜像,仅安装 Cython,并使用 Cython 编译项目,可以起到保护项目源代码、提高项目运行速度的作用。第二阶段使用精简版本的镜像,并安装完整的项目依赖。

dockerfile
ARG PYTHON_VERSION=3.13.11
ARG PYPI_MIRROR_URL=https://pypi.org/simple

# build stage
FROM python:${PYTHON_VERSION}-trixie as builder
ARG PYPI_MIRROR_URL
WORKDIR /app

COPY . ./

RUN pip -V && \
    python -m pip install -i ${PYPI_MIRROR_URL} --upgrade pip && \
    pip config set global.index-url ${PYPI_MIRROR_URL} && \
    pip install Cython && \
    python setup.py build_ext -b lib && \
    mv poetry.lock lib/ && \
    mv pyproject.toml lib/

# production stage
FROM python:${PYTHON_VERSION}-slim-trixie
ARG PYPI_MIRROR_URL
WORKDIR /app

COPY --from=builder /app/lib /app

RUN pip -V && \
    python -m pip install -i ${PYPI_MIRROR_URL} --upgrade pip && \
    pip config set global.index-url ${PYPI_MIRROR_URL} && \
    pip install poetry

RUN python -m venv venv && \
    . venv/bin/activate && \
    poetry install --only main --no-interaction --no-ansi

ENV PATH=$PATH:/app/venv/bin
EXPOSE 8080

CMD [ "sanic", "server:app", "--host=0.0.0.0", "--port=8080", "--fast" ]