
你永远不知道一个没有项目规范的Python仓库能烂到什么程度。一个utils.py塞满5000行函数全局变量从A到Z排列import语句像蜘蛛网一样交叉引用main.py里混着单元测试和数据库连接——这不是段子是每天都在发生的代码灾难。结构混乱的项目三个月后连原作者都看不懂自己写的逻辑。不遵循工程规范的Python代码本质上是在给未来的自己埋定时炸弹。目录结构用文件系统讲述项目故事一个规范的项目目录应该让人一眼就明白业务边界。不要用src套lib套core这种抽象层除非你打算把项目变成俄罗斯套娃。最可靠的入门模板是Cookiecutter-PyPackage或PyPa推荐的扁平化布局项目根目录放README.md、pyproject.toml、setup.cfg这类元信息项目包目录直接以项目名命名例如mypackage/。包内按功能模块划分子包比如mypackage/api/、mypackage/models/、mypackage/services/。测试目录tests/与包目录平级每个测试文件命名要对应被测模块test_services.py测试services模块。有些团队喜欢把所有业务逻辑塞进一个app.py然后衍生出app_v2.py、app_final.py——请立刻停止这种行为。版本控制不是靠文件名后缀实现的那是Git的职责。规范的目录结构强制开发者思考依赖关系如果models和services互相导入说明抽象层次错了。建议在包目录下加一个__init__.py即使是命名空间包也建议显式写用来做公共导出。同时根目录下必须保留docs/、scripts/、requirements/等基础设施目录但避免出现tmp/、archive/这类容易膨胀的文件夹。文件命名从蛇形到一致性的强迫症Python社区约定俗成用snake_case命名模块和包文件但总有人坚持用camelCase或middle-case-with-dash。这种不一致导致import语句看起来像在玩拼图游戏——from My-Code_Lib import helperUtil。违反PEP 8命名约定的代码在代码审查时的第一印象就已经输了。模块名应简短、小写、不含下划线除非必须区分如http_client.pyvshttp_server.py。测试文件命名必须包含test_前缀或_test后缀让pytest能自动发现。数据文件、配置文件的命名也要遵循项目统一约定比如config.yamlvsconfig.prod.yaml。别小看命名对代码可维护性的影响。我曾经见过一个项目里有utils.py、helper.py、common.py、tools.py四个文件里面内容高度重叠——这种命名等于告诉维护者“别区分了都扔进来”。每个文件只做一件事文件名就是它的说明文档。按功能领域拆分data_loader.py、feature_engineering.py、trainer.py明显比helper_part1.py清晰一百倍。代码风格PEP 8是底线但工具才是护城河人工对齐缩进、手动删除末尾空格、逐个检查行长是否超过79字符——这些工作在2025年还靠人肉做属于自虐行为。PEP 8不是可选项而是Python社区的通用语。但更关键的是依赖工具强制执行风格检查。用black做自动格式化它不会问你是否喜欢单引号还是双引号——直接给你定死消除一切无意义的争论。配合isort自动排序import语句标准库、第三方库、本地模块用空行隔开按字母顺序排列。flake8或ruff做静态检查捕捉未使用的变量、语法错误、复杂度过高的函数。我见过最糟糕的反例一个团队花两小时在代码审查里争论if x is not None还是if not x is None。代码审查的时间应该花在逻辑正确性和架构抽象上而不是格式细节。在pyproject.toml或setup.cfg中配置好[tool.black]、[tool.isort]、[tool.ruff]然后集成到CI流水线里。但凡PR触发了格式违规直接阻断合并。这样团队就自动形成肌肉记忆写代码、保存、black .格式化、提交。风格统一后阅读代码的认知负荷至少下降30%。导入与依赖管理别让你的requirements.txt变成黑洞人们常常忽略import语句本身也是项目的“架构图”。PEP 8要求顺序标准库、第三方库、本地模块每组之间空一行。但更重要的是避免通配导入from module import这会把命名空间污染成公共厕所——你不知道哪些符号被引入了也不知道是否覆盖了已有的变量。同样避免循环导入模块A导入BB又导入APython在运行时可能会抛出ImportError。解决方法通常是重构公共逻辑到第三个模块或者使用延迟导入在函数内部导入。依赖管理方面requirements.txt可以工作但不够规范。推荐使用pipenv、poetry或PDM它们能生成锁定文件Pipfile.lock或poetry.lock确保开发、测试、生产环境的依赖版本完全一致。没有锁定文件的Python项目随时可能因依赖升级而崩溃。将依赖分组requirements/dev.txt包含pytest、black等开发工具requirements/prod.txt只包含运行时的必要包。或者用pyproject.toml的[project.optional-dependencies]来区分。永远不要在生产环境安装pytest或ipython这既是安全风险也是性能浪费。代码组织函数、类、模块的清晰边界一个函数超过50行就该考虑拆分了。一个类超过200行说明它可能违反了单一职责原则。一个模块超过800行基本上可以判定为设计失败。这些不是严格限制但反映了代码组织的健康度。规范的做法是每个函数只做一件事并且函数名是动词短语compute_average,validate_email。类名用驼峰UserService,DatabaseConnector方法名用蛇形。模块内部按“公开API在前私有辅助在后”的顺序排列__all__变量明确导出接口。我还观察到一种坏习惯把所有业务逻辑写在if __name__ __main__下。这个块应该只用于短脚本或命令行入口真正的逻辑应该放在函数或类里方便单元测试。if __name__ __main__不是主函数的替代品而是模块的“自测开关”。如果要创建命令行工具使用click或typer库把入口逻辑单独放在cli.py或main.py中然后通过setup.py的entry_points注册。注释与文档别让未来的人咒骂现在的你“Write code that documents itself”这句话被误解了。代码本身能表达逻辑但表达不了“为什么”——为什么选择这种算法为什么这个边界条件被忽略为什么这个函数参数顺序如此奇怪注释应该解释“为什么”而不是“是什么”。例如x 1 # 增加x这种注释毫无价值。好的注释是# 根据业务规则A-007此处需要忽略0值避免除零错误。文档规范上每个公共模块、类、方法必须包含docstring。推荐使用Google风格或NumPy风格配合Sphinx或mkdocs自动生成API文档。我曾经接手一个项目所有函数的参数名都是a,b,c文档里写“这个函数处理数据”——处理什么数据怎么处理全靠猜。没有文档的Python库本质上是个黑箱。在pyproject.toml中配置[tool.pydocstyle]强制要求docstring的存在。同时README.md应该包含项目简介、安装步骤、快速开始示例和贡献指南。docs/目录下放更全面的设计文档、变更日志和架构说明。测试结构让测试成为项目的第二层文档测试代码在项目里常常被当作二等公民扔在一个扁平的tests/文件夹里文件名叫test1.py,test2.py。这等于放弃了测试的组织价值。测试目录的结构应该镜像源文件的结构tests/test_services/test_user_service.py对应mypackage/services/user_service.py。每个测试函数以test_开头命名清晰表达测试场景例如test_create_user_returns_id。使用pytest的fixture机制代替setUp/tearDown利用conftest.py共享跨模块的fixture。覆盖率不是目标而是基线。如果一个项目没有测试任何重构都是在走钢丝。在CI中配置pytest --covmypackage --cov-fail-under80强制要求测试覆盖率不低于某个阈值。但更重要的是测试的质量不是每个函数都必须有单元测试但关键业务逻辑、边界条件、异常处理路径必须覆盖。测试本身也要遵守代码风格规范不然未来的维护者会忽视测试的存在。配置与环境12-Factor App原则在Python中的落地硬编码数据库连接字符串、API密钥、文件路径是Python项目最常见的反模式。配置应该从环境变量中读取而不是写在源码里。使用python-dotenv库在本地开发时加载.env文件但永远不要将.env提交到版本控制。将环境变量分组到settings.py或config.py中通过os.environ.get读取并赋予默认值。如果配置项较多考虑用pydantic-settings定义带类型校验的配置模型还能自动从环境变量映射。另一个常见问题是在不同环境使用不同的配置开发用SQLite生产用PostgreSQL。最好使用统一的配置接口通过DJANGO_SETTINGS_MODULE或自定义的ENV变量来切换配置模块。项目的可配置性决定了它的可移植性。如果换一台机器就要修改十几个文件说明你还没有掌握12-Factor App的配置管理精髓。工具链集成用自动化消灭人为偏差手动运行black、isort、pylint、mypy、pytest——如果每个开发者都自己敲命令总会有人漏掉某个步骤。解决方案是统一的Makefile或Taskfile.yml定义make format,make lint,make test,make typecheck等目标。在CI里也要执行同样的命令。让工具去约束人而不是让人去记住工具。例如pre-commit钩子在每次git commit前自动运行钩子脚本如果格式检查或类型检查失败则禁止提交。这样能在代码进入仓库前就把大部分低级问题过滤掉。类型检查是一个被严重低估的规范。Python是动态类型语言但mypy的静态类型检查可以在开发阶段捕获大量空指针、类型错误、参数不匹配问题。在pyproject.toml中启用--strict模式强制要求函数签名带类型注解。虽然初期要花时间加注解但一个带完整类型注解的Python项目其可维护性不亚于TypeScript项目。版本控制与开发流程规范不是写在文档里而是写在分支里代码规范最终要通过版本控制来落地。Git的分支策略、PR模板、代码审查清单都影响着项目规范的执行力。例如在PR模板中包含“是否符合PEP8”、“是否有类型注解”、“是否有测试”、“配置文件是否涉及敏感信息”这类问题。将规范检查集成到PR的门禁中比任何培训都有效。使用gitignore排除__pycache__/,.env,.pyc,.mypy_cache/,.egg-info/等自动生成的文件避免污染仓库。最后请记住规范不是束缚而是自由。因为有了统一的目录结构新成员可以立刻知道去哪里找配置因为有了代码风格工具没有人再为缩进争吵因为有了测试结构和类型注解重构时心里有底。一个规范的Python项目看起来就像一套搭建好的脚手架——每一根螺栓都在正确的位置每一个模块都自成体系整个项目像一台精密的机器一样运转。这才是专业开发者的尊严。