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+:
winget install Python.Python.3.13Ubuntu/Debian:
sudo apt install python3CentOS/RHEL:
sudo yum install python3macOS:
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.133. Python 开发基本规范
3.1 核心规范
- 包和模块的命名必须符合 PEP8 规范;
- 项目根目录下必须有
pyproject.toml文件,用于管理项目依赖; - 每个包下都必须有
__init__.py文件,可以为空文件; - 包和模块的名称尽量和关键字、标准库和常见第三方库的名称不同,防止导入冲突或混淆;
- 保持你的代码在测试覆盖之下,推荐使用
pytest; - 谨慎地使用并发,特别是针对线程和异步;
3.2 代码风格规范
所有的代码风格遵循 PEP8,以下内容均在此基础上进行补充:
- 统一使用 Black 风格的代码(下文提供指定格式化工具介绍);
- Python 代码一律缩进使用 4 个空格;
- 在项目中优先使用绝对导入,避免使用相对导入;
- 使用
r前缀表示正则表达式,使用R前缀表示原始字符串; - 仅作为类型使用的导入需要单独处理,下面是三种方法:
- 使用
from __future__ import annotations,然后将类型导入放在if TYPE_CHECKING:语句下面; - 将类型导入放在
if TYPE_CHECKING:语句下面,将每个类型注解使用字符串包裹; - 将类型导入放在文件的最后; 通过这几种方法可以防止循环导入,并避免导入不需要的依赖。相当于 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()转为线程支持:pythonimport 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:
{
"recommendations": [
"ms-python.python",
"charliermarsh.ruff",
"fill-labs.dependi",
"EditorConfig.EditorConfig",
"tamasfe.even-better-toml",
"streetsidesoftware.code-spell-checker"
]
}.vscode/settings.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:
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 = false4.2 包管理器
4.2.1 使用 uv
我们推荐使用 uv 作为唯一的包管理器。
Linux 和 macOS 使用 curl 安装:
curl -LsSf https://astral.sh/uv/install.sh | shWindows 使用 powershell 安装:
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"也可以使用 pip 安装:
pip install uv在 Windows 上 UV 默认缓存在 C 盘,这样会导致安装的包无法使用硬链接,无法使用 UV 节省空间的优势。可以将缓存目录设置到工作用的盘符上。例如配置环境变量,将缓存目录设置到 D 盘:
UV_CACHE_DIR=D:\.uv-cacheUV 会严格进行依赖解析,确保每个系统上都能安装对应的 Python 包,并生成锁文件 uv.lock。
有时我们需要针对某些包进行解依赖,以便不同系统上都能安装到正确的版本,例如 PyTorch:
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 = true4.2.2 初始化项目
初始化新项目:
uv init初始化一个包项目:
uv init --package只有包项目才能构建为 Wheel 进行分发。
指定 Python 版本:
uv init --python 3.134.3 多包管理
uv 支持使用 Workspace 管理 Monorepo(即单个仓库多个包)项目。
推荐在父项目(即最外层项目)配置:
[tool.uv.workspace]
members = [
"packages/*",
]新建文件夹 packages/,在此文件夹执行初始化命令会自动注册子包到父项目中:
cd packages/
uv init child_proj --package4.4 代码风格检查
TODO:介绍 sort-imports、black、ruff 等工具。
Python 代码格式化工具有很多,比如:
autopep8flake8autoflakepyflakespycodestylepylintisortblackyapfRuff
mypy 类型检查:https://github.com/run-llama/llama_index/blob/main/pyproject.toml
4.5 提交前检查:pre-commit 钩子
pre-commit 是一个用于管理 Git 钩子的工具,可以在提交代码前自动运行代码格式化、代码检查等工具。
推荐使用 uv 安装 pre-commit:
uv add --dev pre-commit使用 pre-commit 很简单,只需要在项目根目录下创建 .pre-commit-config.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在项目根目录下执行:
pre-commit install现在,每次提交代码时,pre-commit 将会自动运行配置的钩子。 注意 exclude 字段中需要添加项目中所有需要忽略的文件,使用正则表达式匹配。 现在可以手动运行测试:
pre-commit run --all-files自动更新规范版本:
pre-commit autoupdate4.6 类型检查
TODO:介绍 Pylance 和 Mypy.
5. Docker 镜像选型和构建
拉取当前推荐的版本:
docker pull python:3.13.11精简版本:
docker pull python:3.13.11-alpine3.23构建使用的版本:
docker pull python:3.13.11-trixie发布版本:
docker pull python:3.13.11-slim-trixieDockerfile 示例
下面提供一个 Dockerfile 样例,这是一个基于 Sanic 的 Web 项目。此样例使用了多阶段构建,第一阶段使用构建版本的镜像,仅安装 Cython,并使用 Cython 编译项目,可以起到保护项目源代码、提高项目运行速度的作用。第二阶段使用精简版本的镜像,并安装完整的项目依赖。
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" ]