包名必须全小写且不鼓励使用下划线,这是pep 8的强制设计约束,旨在保障模块导入一致性,避免在旧版setuptools等工具中引发路径解析异常或隐式命名冲突。

包名必须全小写,不能含下划线(除非极少数可读性刚需)
PEP 8 明确规定包名应为简短、全小写的标识符,**不鼓励使用下划线**。这不是建议,而是设计约束:下划线会破坏 Python 的模块导入机制在部分环境下的行为一致性,尤其在旧版 setuptools 或某些打包工具中可能触发隐式命名冲突或路径解析异常。
常见错误现象包括:ImportError: No module named 'my_package'(但目录存在),或 pip install 后无法 import my_package,实际是因系统将 my_package 解析为两个独立标识符。
- 正确示例:
mypackage、datatools、apiclient - 错误示例:
my_package、DataTools、MyPackage - 唯一可接受的例外:当名称天然含连字符且转为下划线后仍保持单语义,如
httpx已成事实标准,但http_x仍属违规
模块名与包名风格必须统一,且不强制匹配类名
模块名(即 .py 文件名)和包名共享同一套规则:全小写、无下划线。有人误以为“类叫 UserManager,文件就该叫 user_manager.py”,这混淆了层级——文件名属于模块层,类名属于代码内层,两者无映射义务。强行加下划线反而割裂包结构。
例如你建了一个包 auth,里面放 token.py 和 session.py,这是干净的;若写成 auth_token.py,它就不再是 auth 包下的模块,而是一个顶层模块,from auth import token 就会失败。
- 包内模块应聚焦职责,命名如:
auth/token.py、auth/roles.py - 避免把包名塞进模块名:
auth/auth_token.py是冗余且易引发循环导入 - 类名仍按驼峰写:
class TokenValidator:—— 它和token.py文件名不冲突
子包命名要警惕“伪嵌套”和大小写混用
Python 不支持带点号的包名(如 my.api),所以 my.api 实际是包 my 下的子包 api。这意味着子包名本身也必须符合全小写无下划线规则,且不能与父包同名或形成歧义路径。
典型陷阱是大小写混用导致跨平台失败:在 macOS 或 Windows 上 MyPackage 和 mypackage 可能被当作同一目录,但在 Linux 上严格区分,import mypackage 在 CI 环境直接报错。
- 安全结构:
mypackage/→mypackage/api/→mypackage/api/client.py - 危险结构:
MyPackage/、my-package/、my_package/api_v2/ - 注意
__init__.py存在与否不影响命名规则,但缺失会导致子包不可导入
工具链里最容易忽略的命名检查点
很多团队配了 black 和 flake8,却漏掉对包/模块名的静态校验。这些名字不会出现在代码行里,因此 flake8 默认不报,pylint 也仅在导入时模糊提示。真正能抓到的是 pip install -e . 阶段的构建失败,或 twine check 对分发包元数据的拒绝。
最轻量的预防方式,是在 pyproject.toml 里加一条 pre-commit 钩子:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-case-conflict
- id: check-yaml
- repo: https://github.com/pre-commit/mirrors-pycodestyle
rev: v2.11.1
hooks:
- id: pycodestyle
-
check-case-conflict拦截大小写敏感问题(如MyPackage和mypackage并存) - 手动验证包结构是否合法:
python -c "import mypackage; print(mypackage.__file__)" - 发布前必跑:
python -m build && twine check dist/*——twine会明确报出非法包名
包名一旦发布就几乎无法更改,重命名成本远高于函数或变量。所有看似“只是个名字”的决定,其实是在给整个依赖图钉钉子。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











