
当 Python 包的 __init__.py 未显式导入子模块时,这些模块虽物理存在,却不会被自动添加到包命名空间中,导致 from package import submodule 失败或 dir(package) 不显示该模块——这是由 Python 导入机制的设计决定的,而非路径或语法错误。
当 python 包的 __init__.py 未显式导入子模块时,这些模块虽物理存在,却不会被自动添加到包命名空间中,导致 from package import submodule 失败或 dir(package) 不显示该模块——这是由 python 导入机制的设计决定的,而非路径或语法错误。
你遇到的问题非常典型:在 database/model/ 包中,role.py 文件确实存在且可独立导入(如在其他文件中 import database.model.role 成功),但在执行 from database import model 后,model.role 却不可访问,dir(model) 也未列出 role ——而 session 和 user 却能正常出现。根本原因在于:Python 包的命名空间不是自动“扫描”子模块填充的,而是完全由其 __init__.py 文件的内容定义的。
? 为什么 role 没有出现在 model 的命名空间中?
观察你的目录结构:
database/
└── model/
├── __init__.py ← 关键!它决定了 `database.model` 导出什么
├── role.py
├── session.py
├── user.py
└── model_abstract.pydir(model) 输出中包含 'session'、'user'、'model_abstract',说明 database/model/__init__.py 中已显式导入了它们(例如写了 from . import session, user, model_abstract)。但唯独缺少 role 的导入语句,因此 role 模块虽存在,却未被“注册”进 model 包的公共接口。
✅ 关键原则:
__init__.py是包的“门面”和“导出清单”。它不导入的模块,对外即不可见;即使你用import database.model.role能成功,那也只是“按路径硬加载”,并不会让role成为database.model对象的一个属性。立即学习“Python免费学习笔记(深入)”;
✅ 正确修复:在 database/model/__init__.py 中显式导入 role
打开 database/model/__init__.py,添加以下任一形式(推荐第一种):
# database/model/__init__.py # ✅ 推荐:显式导入并暴露为子模块 from . import role, session, user, model_abstract # 或者(等效): # import .role # import .session # import .user # import .model_abstract # 可选:控制 from database.model import * 的行为 __all__ = ['role', 'session', 'user', 'model_abstract']
保存后,再次运行:
from database import model print(dir(model)) # 现在将包含 'role' x = model.role.Role.get_by_id(1) # ✅ 成功
⚠️ 注意事项与最佳实践
-
不要依赖“隐式发现”:Python 永远不会 自动把同目录下所有
.py文件注入包命名空间。这是明确的设计选择,旨在提升可维护性与可预测性。 -
__all__是可选但强烈推荐的:它明确定义了from database.model import *允许导入的内容,也作为文档向使用者声明公共 API。 -
避免循环导入陷阱:你提到“为避免循环导入才统一通过包导入”,这恰恰是
__init__.py的核心价值——它让你集中管控导入顺序与依赖边界。例如,若role.py依赖user.py,应在__init__.py中先导入user再导入role,或在role.py内部延迟导入。 -
验证方式:执行
python -c "from database.model import role; print(role.__file__)"可快速确认是否生效。
? 总结
| 现象 | 原因 | 解决方案 |
|---|---|---|
model.role 报 AttributeError
|
database/model/__init__.py 未导入 role 模块 |
在 __init__.py 中添加 from . import role
|
dir(model) 缺少某模块名 |
包的命名空间仅由 __init__.py 显式构建 |
将需暴露的子模块全部写入 __init__.py
|
其他文件能单独导入 role
|
import database.model.role 是绝对路径导入,不依赖包接口 |
但跨模块调用应走包统一接口,确保一致性 |
一句话牢记:__init__.py 不是摆设,它是包的契约;你没写的,Python 就当它不存在。


















