Python 进阶之 __init__.py

一、模块和包

  • 模块:就是一个 .py 文件,里头写了变量、函数、类等等。比如你有个 math_tools.py,专门放数学函数,那它就是一个模块。
# math_tools.py
def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

用的时候就很直接:

import math_tools

print(math_tools.add(3, 5))  # 输出 8
  • 包:一整个文件夹,里面可以塞好多个模块文件。比如你有个 math_utils 文件夹,里面有一堆数学相关的 .py 文件,那么这个文件夹就是一个包。

二、为什么出现了 init.py

在 Python 3.3 以前,想让一个文件夹被认出来是包,你必须在里面放一个 init.py。这就是它的“出生证”。

Python 3.3 之后,官方稍微放松了规定,即使没有它,解释器也能识别出“命名空间包”。 但现实是,大多数公司项目还是会老老实实加上这个文件,原因有三:

  1. 明确标记:防止工具或解释器搞混,把包当普通文件夹。
  2. 兼容旧版本:有些老项目或依赖没更新,少了它会直接跪。
  3. 自定义导入逻辑:可以在包加载时执行额外的初始化动作。

三、init.py 有什么用?

1. 把包当做模块使用

如果你不想每次都 import math_utils.basic 这样一层层写,可以在 init.py 里提前暴露子模块内容。

# math_utils/__init__.py
from .basic import add, subtract
from .advanced import power

于是用起来就变成:

import math_utils

print(math_utils.add(2, 3))
print(math_utils.power(2, 3))

这样外部调用者就不需要知道包的内部结构,体验更丝滑。

2. 包初始化操作

有些项目会在 init.py 里写启动信息、加载配置等:

# math_utils/__init__.py
print("数学工具包加载成功!")

只要 import math_utils,这行代码就会被执行。 这种方式虽然简单粗暴,但对调试和日志记录特别方便。

3. 动态导入子模块

在大型项目中,包里的模块多到数不过来,一个个写导入太傻。于是就有人用动态导入一把梭:

# math_utils/__init__.py
import os
import importlib

package_path = os.path.dirname(__file__)

for module in os.listdir(package_path):
    if module.endswith(".py") and module != "__init__.py":
        module_name = module[:-3]
        importlib.import_module(f"{__name__}.{module_name}")

4. 控制暴露的内容

有时候,我们并不想让外部随便访问所有模块,就可以用 all 来管控:

# math_utils/__init__.py
from .basic import add, subtract

__all__ = ["add", "subtract"]

这样 from math_utils import * 时,只能用 add 和 subtract,其他隐藏得严严实实。

5. 懒加载

懒加载能显著提升性能,尤其是大项目启动的时候:

# math_utils/__init__.py
import importlib

def lazy_import(name):
    return importlib.import_module(f"{__name__}.{name}")

basic = lazy_import("basic")

只有在第一次真正用到 basic 时,才会去加载它。

6. 版本管理

# math_utils/__init__.py
__version__ = "1.0.0"

调用时:

import math_utils
print(math_utils.__version__)

方便清晰,还能在多人协作时快速确认版本。

几个建议

  1. 定要加,哪怕是空的,这样对兼容性和可维护性都好。
  2. 尽量简洁,不要在里面搞太多复杂逻辑,否则导入包的时候会莫名变慢。
  3. 合理用 __all__,防止暴露太多内部细节。
  4. 动态导入要慎用,虽然方便,但可能带来隐形性能问题。