from package import * 只导入 __all__ 列表中明确声明的名字,未列出的即使存在也不会被导入;若未定义 __all__,则仅自动导入同级非下划线开头的 .py 模块,不递归子包。

from package import * 为什么只导入部分模块
因为 Python 默认不会把包里所有模块都暴露给 from package import *,它只认 __all__ 列表里明确写进去的名字。没加进去的,哪怕文件存在、能 import 成功,也不会被星号导入捕获。
这跟“有没有 __init__.py”无关,也跟文件名是否合法无关——核心就一条:__all__ 是白名单,不是黑名单。
-
__all__必须定义在包的__init__.py文件中(类型为list或tuple),且元素必须是字符串 - 如果
__init__.py里根本没定义__all__,Python 会退而求其次,尝试扫描当前目录下所有.py文件名,但**仅限于不以下划线开头的文件**(即忽略_helper.py这类) - 即便走自动扫描逻辑,它也**不会递归子包**,更不会导入模块内部的函数或类——只导入同级模块对象本身
__all__ 里该写模块名还是对象名
写什么,取决于你希望用户通过 from package import * 拿到什么。它控制的是“导入后出现在当前命名空间里的名字”,不是“要加载哪些文件”。
比如 package/__init__.py 里写了 __all__ = ["utils", "config"],那 from package import * 后,utils 和 config 这两个模块对象就可用;但如果想让用户直接用 load_yaml(),就得把函数名放进 __all__,并提前在 __init__.py 中导入它:
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
from .utils import load_yaml __all__ = ["load_yaml"]
- 写模块名(如
"utils"):导入的是模块对象,使用时需utils.load_yaml() - 写函数/类名(如
"load_yaml"):前提是已在__init__.py中执行了from .utils import load_yaml,否则运行时报NameError - 混写允许,但容易混乱;建议统一风格,多数库倾向导出易用的对象名而非模块名
常见失效场景和调试方法
最典型的“明明写了 __all__ 却不生效”,往往卡在路径或执行顺序上。
- 检查是否误改了
__init__.py的编码或加了 BOM 头,导致__all__定义没被正确解析(可打印dir(package)看实际内容) - 确认
__all__是顶层变量,没被缩进、没被包裹在函数或条件块里(例如if False:下的定义无效) - 注意相对导入路径错误:若
__init__.py中写from utils import xxx(缺点),Python 会去父级找utils,而不是当前包下的utils.py,结果xxx根本没导入成功,__all__里列了也白列 - 执行
from package import *后,用print([x for x in dir() if not x.startswith('_')])快速验证导入效果
替代方案比 __all__ 更可控
依赖 __all__ + 星号导入本质是妥协方案,容易失控。真正需要清晰 API 边界的项目,应该主动设计导入入口。
- 在
__init__.py中显式导入并重命名:例如from .core import run as execute,再把"execute"加入__all__,避免用户直呼内部模块名 - 用
__getattr__(Python 3.7+)做懒加载:对非核心功能延迟导入,既减少启动开销,又能按需暴露接口 - 彻底放弃
import *,改用文档明确列出推荐导入方式,比如from package.core import execute—— 大多数成熟库(如requests、click)都不支持星号导入
别低估 __all__ 的维护成本:每次新增一个公开接口,不仅要改实现文件,还得同步更新 __init__.py 里的字符串列表,漏一次,用户就用不了。真正的边界控制,得靠导入逻辑本身,而不是一个靠人维护的字符串清单。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










