Python 中 init.py 文件的作用是什么
init.py 标记目录为 Python 包,其代码在包被导入时执行。它可用于包初始化、暴露公共接口、控制导入行为;Python 3.3+ 的命名空间包允许省略,但常规包仍推荐保留。
直接回答:__init__.py 把一个普通目录变成 Python 可导入的包(package)。 导入包时该文件会首先执行,因此常用来放置包级初始化代码、汇总导出公共接口。文件可以为空,也可以是包的"门面"。
基本作用:声明"这是一个包"
mypackage/
├── __init__.py
├── module1.py
└── module2.py
有了这个文件(哪怕为空),就能:
import mypackage.module1
from mypackage import module2
导入 mypackage.module1 时,Python 先执行 mypackage/__init__.py,再执行 module1.py 的导入。多级包同理——import a.b.c 会依次执行 a、b 两级的 __init__.py。
常见用法
1. 包级初始化
# mypackage/__init__.py
import logging
logging.getLogger(__name__).addHandler(logging.NullHandler())
DEFAULT_TIMEOUT = 30
print('mypackage initialized') # 只在首次导入时执行一次
2. 汇总公共接口(门面模式)
把深层模块的对象提到包顶层,简化使用方的导入路径:
# mypackage/__init__.py
from .module1 import Client
from .module2 import helper
__all__ = ['Client', 'helper']
使用方就可以写 from mypackage import Client 而不是 from mypackage.module1 import Client。
3. 控制 from package import * 的行为
__all__ 列表决定星号导入时暴露哪些名字。
不写 init.py 行不行
Python 3.3 起引入了命名空间包(namespace package):没有 __init__.py 的目录也能被导入,多个同名目录还能合并成一个包。这在插件系统、monorepo 拆包发布时有用。
但常规项目仍推荐显式写 __init__.py:
- 意图清晰,工具链(部分打包器、静态检查器)兼容性更好
- 避免目录被意外当作命名空间包合并
- 需要包级代码时本来就得有
注意事项
- 不要在 init.py 里做重活:首次加载通常会执行并缓存在 sys.modules 中;后续普通 import 通常复用缓存,放重型逻辑会拖慢所有子模块的导入
- 小心循环导入:
__init__.py导入子模块、子模块又 import 包本身,容易触发 ImportError - 命名必须是
__init__.py,不是init.py——前后各两个下划线
常见问题(FAQ)
Q:init.py 里的代码会执行几次?
A:同一解释器中成功导入后通常缓存于 sys.modules。importlib.reload、删除缓存或使用其他模块身份重新加载可再次执行,不能绝对说整个进程只执行一次。
Q:init.py 为空会报错吗?
A:不会,空文件完全合法,也是很多项目的实际状态——纯粹起"标记为包"的作用。
Q: Django / Flask 项目里 models 目录的 init.py 常导入所有模型,为什么?
A:这是"汇总接口"用法的典型:既让 from .models import User 可用,也确保框架在扫描模型、生成迁移时所有模型类都已被加载注册。
参考资料
本文依据官方资料核对,未进行现场运行测试;代码与配置示例需结合实际版本、权限和环境验证。