Python里__init__.py到底有什么用?

Python里__init__.py到底有什么用?

AirTouch Lv2

__init.py__ 的这个问题挺常见的,说简单简单,说难也难,尤其是对Python新手来说简直就是一团迷雾。给大家唠一唠__init__.py的用途和奥秘,顺便帮大家“踩踩坑”,看看有哪些用法需要注意。

__init.py__是个啥

__init__.py,顾名思义,这个文件名就透露出它是用来“初始化”的。在Python里,它主要用于标识一个目录是一个包(Package)

你在项目里新建了一个文件夹,要让它成为一个可供导入的模块包,最简单的办法就是在里面加一个 __init__.py 。

比如,咱们有个项目结构如下:

1
2
3
4
my_project/    
├── __init__.py
├── module1.py
└── module2.py

当我们想要在外部使用my_project这个包时,就可以这样导入:

1
import my_project

有了 __init__.py 的存在,Python才知道 my_project 是一个包,而不是一个普通的文件夹。

__init__.py 能做些啥?

很多初学者以为这个文件只能“声明”包,实际上它的用法比你想象的要丰富得多。在 __init__.py 里你可以编写代码,它会在包被导入时自动执行。这有什么用呢?我给大家举几个常见的例子:

1.模块初始化操作

假如你有一个需要初始化配置的工具包,你可以在__init__.py里直接搞定这些初始化工作。

1
2
3
4
5
6
7
# 例子:my_project/__init__.py
import os

# 初始化配置文件路径
config_file = os.path.join(os.path.dirname(__file__), 'config.yaml')

print("正在初始化")

这样,当你一导入my_project时,config.yaml就被自动加载了,是不是很方便?你再也不用在每个子模块里重复配置路径了。

2.控制子模块导入

通过在 __init__.py 中用 from .sub_module import some_function 的方式,可以直接在 import package_name 的时候就将所有常用的子模块或者函数导入,这样你就能从包的顶级目录直接访问子模块的内容了:

1
2
3
4
5
6
# 例子:my_project/__init__.py
from .module1 import func1from .module2 import func2

# 这样你就可以这样用:
# import my_project
# my_project.func1()

3.包级别变量和函数的初始化

你还可以在 __init__.py 里设置一些全局变量,或者定义一些包级别的工具函数。

1
2
3
4
5
# 例子:my_project/__init__.py
package_name = "my_project"

def show_info():
print(f"欢迎使用{package_name}!🎉")

这样在任何地方,只要你导入了my_project,就能直接调用show_info()了。

__init__.py 的一些“坑”

俗话说得好,“有光的地方就有阴影”。别看 __init__.py 这么实用,里面也有不少坑,尤其是循环导入(Circular Import)的问题,这个坑可是踩一次怀疑人生那种级别的。

什么是循环导入?

假设你有两个模块module1和module2,然后你在module1.py中写了这样一段代码:

1
2
# module1.py
from .module2 import some_function

然后你又在module2.py里这样写:

1
2
# module2.py
from .module1 import another_function

这就会导致Python在导入包的时候出现死循环,结果是两边互相等待对方加载,最终就会报错或者无法正常导入。

如何解决

一般来说,解决方案有两种:

  • 延迟导入:将导入语句放在函数内部,而不是放在文件头部。

    1
    2
    3
    4
    # module1.py
    def call_function_from_module2():
    from .module2 import some_function
    some_function()
  • 重构代码结构:把相互依赖的部分提取出来,放到一个公共模块里,这样两个模块就不会直接互相依赖了。

__init__.py 和相对导入的关系

另外,再讲一个可能让人头疼的点,相对导入绝对导入。很多小伙伴可能会在 __init__.py 里用相对导入的语法,比如:

1
from .module1 import func1

乍看上去没啥问题,但等到你跑 module1.py 这个文件时,就会发现报错了!因为相对导入的方式要求你必须从顶层包开始导入。而你直接执行 module1.py,Python根本不知道它是从哪个包里来的。

解决方案呢?我建议尽量使用绝对导入,比如这样:

1
from my_project.module1 import func1

这样不管你是直接运行module1.py,还是导入整个my_project,都不会有问题。

总体来说,__init__.py 用得好,它能让整个包管理得井井有条,用得不好,它就会变成你代码里的“绊脚石”。

  • 标题: Python里__init__.py到底有什么用?
  • 作者: AirTouch
  • 创建于 : 2024-10-13 11:57:10
  • 更新于 : 2024-10-13 12:15:54
  • 链接: https://touchware.us.kg/Python里-init-py到底有什么用?/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论