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 可用,也确保框架在扫描模型、生成迁移时所有模型类都已被加载注册。

参考资料

本文依据官方资料核对,未进行现场运行测试;代码与配置示例需结合实际版本、权限和环境验证。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台