首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >一文教你看懂__init__.py这个被大家轻视的文件

一文教你看懂__init__.py这个被大家轻视的文件

原创
作者头像
码匠er
发布2025-05-15 22:59:11
发布2025-05-15 22:59:11
5950
举报

大家好,我是码匠er。在开源工作中,你是否有过下面的经历?克隆了一个Python开源项目,满怀期待准备运行时,却收到了这样的报错:ModuleNotFoundError: No module named 'xxx'。这个问题就是因为我们今天的主角__init__.py文件造成的,今天我们就扒一扒这个文件的神秘面纱!

1. 揭开init.py 的神秘面纱

  1. 标识包身份:当 Python 解释器遇到一个包含init.py的目录时,会自动将其识别为一个包(Package),而非普通文件夹。
  2. 模块预处理:当使用import package语句导入包时,Python 会优先执行init.py中的代码。我们可以在这里完成模块的预加载、全局变量定义等初始化操作。
  3. 控制导出内容:通过定义__all__变量,我们能精确控制from package import *语句导入的模块列表,避免暴露无关接口。

2. 历史变迁小剧场

  • 在 Python 3.3 之前,必须在包目录下创建这个文件(可以是空文件),否则 Python 不会将其识别为包。
  • 从 Python 3.3 开始,根据 PEP 420 的规定,引入了隐式命名空间包(Implicit Namespace Packages),允许没有init.py 的目录也被当作包来导入。

3. 作用和常见用途

3.1 标记目录为Python包

没有__init__.py,Python 不会认为这是一个包,也就无法通过import package_name的方式引入它。

3.2 简化 API 接口暴露

目录结构:

代码语言:python
复制
main.py
demoA/
├── __init__.py
├── module_a.py  # 定义变量A
└── module_b.py  # 定义变量B

如果没有__init__.py文件中,我们在main.py中需要如下才能访问变量,我们需要从Python文件中获取我们需要的资源

代码语言:python
复制
from demoA.module_a import A
from demoA.module_b import B

print(A)
print(B)

如果有__init__.py文件中,我们可以在__init__py文件中将资源汇聚出来。

代码语言:python
复制
from .module_a import A
from .module_b import B

这样我们的demoA就是一个别识别的Python包,我们直接可以从包中获取想要的资源

代码语言:python
复制
from demoA import A
from demoA import B

print(A)
print(B)

有些大型库(如 Flask、Pandas)会在__init__.py中暴露最常用的接口,让用户更方便地使用。

3.3 初始化包级别的变量或配置

可以在__init__.py中定义一些全局变量或配置信息,可以供我们使用。将__init__.py修改为如下代码:

代码语言:python
复制
__version__ = "1.0.0"
__author__ = "码匠er"

print("init demoA")

将调用时的代码修改为如下代码:

代码语言:python
复制
import demoA

print(demoA.__version__)
print(demoA.__author__)

执行结果如下:

我们可以看出,在我们导包时就会执行__init__.py中的代码。所以我们将初始化代码写在这个文件中。

3.4 控制通配符导入

init.py中定义all列表,可以控制from package import *时导入哪些模块或变量。

直接上代码:

代码语言:python
复制
# __init__.py
from .module_a import A
from .module_a import B
from .module_a import add

__all__ = ['A', 'B', 'add']
代码语言:python
复制
# module_a.py
A = 5
B = 6
C = 7


def add(a, b):
    return a + b
代码语言:python
复制
# main.py
from demoA import *

print(A)
print(B)
print(add(A, B))

# print(C) 报错

有时候我们不希望用户直接访问包内的某些模块,可以通过__init__.py进行接口封装,然后使用__all__变量进行控制。

3.5 延迟加载

为了提高性能,有时我们会选择在__init__.py中只导入常用模块,其他模块按需导入。

代码语言:python
复制
# __init__.py

def get_modules():
    from .module_b import B # 如果module_b中的B是一个不常用且很大的模块,我们不需要一直引用,而是需要的时候再调用
    return B()

3.6 单元测试的妙用

在测试框架中,__init__.py可以用于批量导入测试用例。

代码语言:python
复制
tests/
├── __init__.py
├── test_api.py
└── test_models.py

__init__.py文件中,我们可以这样写:

代码语言:python
复制
import unittest
from .test_api import TestAPI
from .test_models import TestModels

def suite():
    test_suite = unittest.TestSuite()
    test_suite.addTest(unittest.makeSuite(TestAPI))
    test_suite.addTest(unittest.makeSuite(TestModels))
    return test_suite

只需执行python -m tests即可运行所有测试。是不是方便了很多呢?

最后

通过这篇文章的学习,我相信你应该知道__init__.py虽然只是一个简单的文件,但它却是 Python 模块化体系中非常关键的一环。无论是构建自己的库,还是理解他人项目的结构,掌握它的用法都能让你事半功倍。还有什么其他的问题可以留言或者找我哦!

🎯 我正在做开源项目,如果你想提升编程能力、参与真实项目、写进简历加分项,我们正在开发多个有趣实用的 Python 开源项目,涵盖多个方向。无论你是初学者还是想积累项目经验,都欢迎你的加入!

💬 感兴趣的话欢迎在后台留言关注我公众号哦

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 揭开init.py 的神秘面纱
  • 2. 历史变迁小剧场
  • 3. 作用和常见用途
    • 3.1 标记目录为Python包
    • 3.2 简化 API 接口暴露
    • 3.3 初始化包级别的变量或配置
    • 3.4 控制通配符导入
    • 3.5 延迟加载
    • 3.6 单元测试的妙用
    • 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档