Python 包结构基础:init.py 作用
文章目录
- 前言
- 一、__init__.py 到底是什么?
- 二、__init__.py 的五大核心作用(2026最新版)
- 2.1 作用一:声明文件夹为Python包(基础)
- 2.2 作用二:控制包的导入暴露(__all__ 变量)
- 2.3 作用三:简化导入路径(最实用的工程技巧)
- 2.4 作用四:包初始化逻辑(启动时执行)
- 2.5 作用五:隐藏内部实现,对外提供稳定API
- 三、Python 3.10+ 下 __init__.py 的新变化(2026重点)
- 3.1 空 __init__.py 完全合法
- 3.2 命名空间包不需要 __init__.py,但不推荐业务使用
- 3.3 __init__.py 中禁止写复杂业务逻辑
- 3.4 推荐搭配 pyproject.toml 使用
- 四、__init__.py 最佳实践(直接照抄用)
- 4.1 最简空包(推荐)
- 4.2 带 __all__ 控制导入
- 4.3 简化导入(最常用)
- 4.4 带版本信息
- 4.5 懒加载高级写法(2026流行)
- 五、新手最容易踩的 __init__.py 坑
- 5.1 坑1:忘记写 __init__.py,导入失败
- 5.2 坑2:循环导入
- 5.3 坑3:__all__ 写错,导致 import * 失效
- 5.4 坑4:在 __init__.py 写大量代码,导致导入极慢
- 5.5 坑5:把 __init__.py 当成普通业务文件用
- 六、真实项目结构示例(2026标准)
- 七、总结:一句话记住 __init__.py
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,[传送门https://blog.csdn.net/HHX_01],(https://blog.csdn.net/HHX_01/article/details/159613021)
前言
很多刚学Python的小伙伴都会遇到一个灵魂拷问:为什么我写的模块别人能导入,我自己写的包就死活导入失败?排查半天,最后发现只是少了一个__init__.py文件。
在Python开发中,__init__.py可以说是最常见又最容易被忽略的文件。尤其是2026年现在,Python 3.10+已经全面普及,命名空间包、pyproject.toml、importlib等新特性层出不穷,很多人还在用十年前的旧写法,不仅代码臃肿,还容易踩坑。
这篇文章我就用最通俗、最接地气的方式,把__init__.py的作用、用法、新版变化、最佳实践、避坑指南一次性讲透。不管你是刚入门的小白,还是工作多年的老开发,都能从中找到能直接用在项目里的干货。
一、init.py 到底是什么?
简单一句话:__init__.py是Python用来标识一个文件夹为「标准包(Package)」的标记文件。
在没有__init__.py之前,一个文件夹只是普通文件夹,Python 不会把它当成包来识别,你也无法正常import里面的模块。
举个最直观的例子:
my_project/ ├── utils/ │ ├── math.py │ └── string.py └── main.py如果你在main.py里写:
importutils.mathPython 会直接报错:ModuleNotFoundError。
但只要在utils文件夹里加一个空的__init__.py:
my_project/ ├── utils/ │ ├── __init__.py │ ├── math.py │ └── string.py └── main.py再导入就完全正常了。
这就是__init__.py最基础、最核心的作用:声明这是一个Python包。
二、init.py 的五大核心作用(2026最新版)
很多老教程只讲“标记包”,但在现代Python开发中,__init__.py的作用远不止于此。下面我按实际开发频率从高到低,把五大核心作用讲清楚。
2.1 作用一:声明文件夹为Python包(基础)
这是最原始、最必须的作用。只要你想让一个文件夹变成可导入的包,就必须有__init__.py。
注意:
从 Python 3.3+ 开始,引入了命名空间包(Namespace Package),可以没有__init__.py也能导入。但在实际工程、发布包、公司项目中,99% 仍然推荐使用__init__.py,因为命名空间包有很多隐式坑,不适合小白和标准工程。
2026年的主流规范依然是:
业务项目必须写__init__.py,库开发建议保留。
2.2 作用二:控制包的导入暴露(all变量)
这是__init__.py最常用、最重要的功能,没有之一。
你一定见过这种写法:
# utils/__init__.py__all__=["math","string"]它的作用是:
当别人使用from utils import *时,只导入__all__列表里的模块。
没有__all__,*导入行为是混乱且不可控的。
真实开发场景示例:
# utils/math.pydefadd(a,b):returna+bdefminus(a,b):returna-b# utils/string.pydefupper(s):returns.upper()# utils/__init__.py__all__=["math","string"]在外部使用:
fromutilsimport*print(math.add(1,2))print(string.upper("hello"))非常干净、规范。
如果没有__all__,import *可能会导入内部变量、测试函数、私有模块,导致命名冲突。
2.3 作用三:简化导入路径(最实用的工程技巧)
这是中高级开发者必用的技巧,可以让用户不用写冗长的层级导入。
比如原本结构:
my_lib/ ├── __init__.py ├── core/ │ ├── __init__.py │ ├── calculator.pycalculator.py里有一个Calculator类。
正常导入要写:
frommy_lib.core.calculatorimportCalculator又臭又长。
我们可以在my_lib/core/__init__.py里写:
from.calculatorimportCalculator再在my_lib/__init__.py写:
from.coreimportCalculator外部用户只需要:
frommy_libimportCalculator层级被彻底抹平,使用体验大幅提升。
这也是2026年主流开源库(如FastAPI、Pandas、Polars)的标准做法。
2.4 作用四:包初始化逻辑(启动时执行)
__init__.py在包第一次被导入时会自动执行,所以可以用来做初始化操作。
例如:
- 加载配置
- 初始化日志
- 检查环境
- 注册插件
- 预加载模型
示例:
# my_lib/__init__.pyimportloggingimportos# 包初始化时执行print("my_lib 正在初始化...")# 设置日志logging.basicConfig(level=logging.INFO)logger=logging.getLogger("my_lib")# 读取环境配置VERSION="1.0.0"DEBUG=os.getenv("MY_LIB_DEBUG","0")=="1"外部导入时:
importmy_lib# 自动输出:my_lib 正在初始化...注意:
初始化逻辑不要太重,2026年的最佳实践是尽量懒加载,避免拖慢导入速度。
2.5 作用五:隐藏内部实现,对外提供稳定API
大型项目一定会区分:
- 公开接口(public):给用户用
- 内部实现(private):自己用,不对外暴露
__init__.py就是最好的隔离层。
示例结构:
ai_toolkit/ ├── __init__.py ├── _internals/ │ ├── __init__.py │ ├── engine.py │ └── utils.py └── api.py_internals是内部实现,不希望用户直接导入。
我们在ai_toolkit/__init__.py只暴露公开API:
from.apiimportModelInfer,Tokenizer用户只能这样用:
fromai_toolkitimportModelInfer无法直接导入_internals,保证了代码安全与后续重构自由。
三、Python 3.10+ 下init.py 的新变化(2026重点)
到了2026年,Python 3.10及以上版本成为绝对主流,__init__.py也有了很多现代化变化。
3.1 空init.py 完全合法
很多人强迫症喜欢删空文件,但保留空__init__.py是现代Python标准规范,不影响性能,也不冗余。
3.2 命名空间包不需要init.py,但不推荐业务使用
命名空间包适合分发包、多模块联合包,但业务系统用起来容易出现:
- 导入路径混乱
- 无法控制
__all__ - 无法做初始化
- IDE识别错乱
所以公司项目一律用传统带init.py 的包。
3.3init.py 中禁止写复杂业务逻辑
现代Python强调懒加载,__init__.py只做三件事:
- 导出公开API
- 定义元信息(version、author)
- 极简初始化
不要再写循环、IO、网络请求、 heavy计算在__init__.py里。
3.4 推荐搭配 pyproject.toml 使用
2026年打包标准是pyproject.toml,__init__.py负责运行时导出,pyproject.toml负责构建时配置。
示例:
[project] name = "my_utils" version = "0.1.0"在__init__.py中:
__version__="0.1.0"保持一致即可。
四、init.py 最佳实践(直接照抄用)
我把2026年工业界最标准的写法整理成模板,你可以直接复制到项目中。
4.1 最简空包(推荐)
# __init__.py# 空文件即可4.2 带all控制导入
__all__=["math","string","file"]4.3 简化导入(最常用)
from.coreimportEnginefrom.apiimportpredictfrom.utilsimportConfig __all__=["Engine","predict","Config"]4.4 带版本信息
__version__="1.0.0"__author__="AI开发者"from.modelimportMyModel __all__=["MyModel"]4.5 懒加载高级写法(2026流行)
避免启动慢:
fromimportlibimportlazy_import __all__=["Model"]Model=lazy_import(".model").Model导入时不加载,第一次使用才加载。
五、新手最容易踩的init.py 坑
5.1 坑1:忘记写init.py,导入失败
这是小白第一大坑。只要是包,必须加__init__.py。
5.2 坑2:循环导入
在__init__.py导入子模块,子模块又导入包,导致死循环。
解决方案:
- 简化
__init__.py - 使用懒加载
- 调整依赖关系
5.3 坑3:all写错,导致 import * 失效
__all__是字符串列表,写错名字就会找不到模块。
5.4 坑4:在init.py 写大量代码,导致导入极慢
现代Python追求极速导入,初始化逻辑尽量丢到内部模块。
5.5 坑5:把init.py 当成普通业务文件用
它是包声明文件,不是业务逻辑文件,不要写函数实现。
六、真实项目结构示例(2026标准)
给你一个可直接用于生产的完整结构:
my_ai_project/ ├── my_ai/ │ ├── __init__.py # 导出API │ ├── core/ │ │ ├── __init__.py │ │ └── engine.py │ ├── utils/ │ │ ├── __init__.py │ │ ├── log.py │ │ └── config.py │ └── api.py ├── pyproject.toml └── main.pymy_ai/__init__.py:
__version__="1.0.0"from.core.engineimportAIEnginefrom.apiimportinferencefrom.utils.configimportSettings __all__=["AIEngine","inference","Settings"]外部使用:
frommy_aiimportAIEngine,inference,Settings干净、优雅、专业。
七、总结:一句话记住init.py
- 标记包:告诉Python这是一个包
- 控制暴露:用
__all__管理导入 - 简化路径:让用户少写层级
- 初始化:包启动时执行一次
- 隔离实现:保护内部代码
2026年的Python开发,__init__.py不是过时产物,而是包结构设计的核心枢纽。写好它,你的项目结构会清晰十倍,别人用你的包也会爽十倍。
只要按照本文的规范去写,你再也不会遇到导入失败、结构混乱、规范不统一的问题。
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,[传送门https://blog.csdn.net/HHX_01],(https://blog.csdn.net/HHX_01/article/details/159613021)
