python 中__init__.py 的用法
python
本文字数:1.7k 字 | 阅读时长 ≈ 7 min

python 中__init__.py 的用法

python
本文字数:1.7k 字 | 阅读时长 ≈ 7 min

1. 什么是init.py?

__init__.py 是 Python 中一个特殊的文件,主要用于以下几个目的:

  1. 标识包__init__.py 文件会让人知道他是一个 python 包
  2. 包初始化:导入包时,__init__.py 会首先被执行。你可以在这个文件中包含包的初始化代码,例如设置包的变量、导入子模块等
  3. 控制子模块的导入:通过在 __init__.py 中使用 __all__ 列表,可以控制 from package import * 语句导入的模块或对象

既然包含 __init__.py 的文件会被当做 python 包,我们就需要知道文件夹和 python 包的区别

2. 文件夹和 python 包的区别

如下例子

project/
    ├── directory/
    │   ├── file1.txt
    │   └── file2.txt
    ├── mypackage/
    │   ├── __init__.py
    │   ├── module1.py
    │   └── module2.py

在 Python 3.3 及以后版本中,可以不写 __init__.py,python 也会将其当作一个包,init.py 是可选的,但在目录中包含一个空的__init__.py 文件可以明确地告诉其他开发人员这个目录是一个 Python 包,而不仅仅是一个普通的目录,此外还有其他的好处,例如可以在 __init__.py 中导入子模块或者控制导入的包等等,下面做详细介绍

3. init.py 使用

假设文件目录如下

project/
    ├── main.py
    └── mypackage/
        ├── __init__.py
        ├── module1.py
        └── module2.py

__init__.py module1.py module2.py 三个文件内容如下所示

# mypackage/__init__.py
__all__ = ['module1', 'module2']

print("Initializing mypackage")
package_variable = "Hello, World!"  # 包级别的变量
from . import module1  # 导入子模块
from . import module2

# mypackage/module1.py
def greet(name):
    return f"Hello, {name}!"

# mypackage/module2.py
def farewell(name):
    return f"Goodbye, {name}!"

下面我们运行如下的文件 python main.py

import mypackage

# 使用包级别的变量
print(mypackage.package_variable)

# 使用导入的模块
greeting = mypackage.module1.greet("Alice")
farewell_message = mypackage.module2.farewell("Bob")

print(greeting)
print(farewell_message)

输出如下所示

Initializing mypackage
Hello, World!
Hello, Alice!
Goodbye, Bob!

下面对上述流程进行解释

  1. 初始化包:当导入 mypackage 时,mypackage/__init__.py 会首先被执行。初始化代码会输出"Initializing mypackage"并设置包级别的变量 package_variable。
  2. 包级别的变量:package_variable 被定义在 __init__.py 中,因此可以通过 mypackage.package_variable 在主文件中访问。
  3. 导入子模块:在 __init__.py 中导入了 module1module2,使得它们可以作为 mypackage 的属性被访问。因此,可以在主文件中使用 mypackage.module1.greetmypackage.module2.farewell

注意:在 Python 3.3 之后,__init__.py 文件变得可选,因此即使 __init__.py 文件不存在,仍然可以导入包中的模块。但是不能在包级别进行初始化操作或定义包级别的变量,因为没有 __init__.py 文件来执行这些操作。但对于简单的模块组织,这种方法是完全可行的。但是在 __init__.py 中导入子模块,使得用户可以通过更简洁的语法导入和使用包中的内容

4. init.py 的进阶使用

4.1 简化导入

我们讲上述的 __init__.py 文件做修改

# mypackage/__init__.py
from .module1 import greet
from .module2 import farewell

这样我们就可以在 main.py 中直接使用 mypackage.greet("Alice"),而不是 mypackage.module1.greet("Alice") 来简化代码了

4.2 控制导入对象

通过定义 __all__ 列表,可以控制使用 from package import * 语句时导入的模块和对象,我们先看几个例子,然后来理解这句话

依然是上面的文件列表,我们仅对 __init__.py 进行修改

  1. 什么都不加
# mypackage/__init__.py
print("Initializing mypackage")
package_variable = "Hello, World!"  # 初始化包级别的变量

如下所示,如果我们想调用 mypackage 中的 module1,是没办法调用的,必须执行 from mypackage import module1 才可以

>>> import mypackage
Initializing mypackage
>>> mypackage
<module 'mypackage' from '/apdcephfs/llm-cfs-nj/person/harryyhwang/test/mypackage/__init__.py'>
>>> mypackage.package_variable
'Hello, World!'
>>> mypackage.module1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'mypackage' has no attribute 'module1'
>>> from mypackage import module1
>>> module1
<module 'mypackage.module1' from '/apdcephfs/llm-cfs-nj/person/harryyhwang/test/mypackage/module1.py'>
  1. 额外引入一个 module1
# mypackage/__init__.py
print("Initializing mypackage")
package_variable = "Hello, World!"  # 初始化包级别的变量

from . import module1

这里我们在初始化文件中引入了 module1,此时 module1 变成了 mypackage 的一个属性,这样我们就可以执行 mypackage.module1 这句话了,但是因为我们在初始化文件中没有导入 module2,所以在主文件中调用 module2 会报错

>>> import mypackage
Initializing mypackage
>>> mypackage
<module 'mypackage' from '/apdcephfs/llm-cfs-nj/person/harryyhwang/test/mypackage/__init__.py'>
>>> mypackage.module1
<module 'mypackage.module1' from '/apdcephfs/llm-cfs-nj/person/harryyhwang/test/mypackage/module1.py'>
>>> mypackage.module2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'mypackage' has no attribute 'module2'. Did you mean: 'module1'?
  1. 引入一个 module1 中的方法
# mypackage/__init__.py
print("Initializing mypackage")
package_variable = "Hello, World!"  # 初始化包级别的变量

from .module1 import greet

这里我们直接将 module1 中的 greet 方法加入到初始化文件中,这样 greet 就成为了 mypackage 的一个属性了,我们可以通过 mypackage 包直接调用它

>>> import mypackage
Initializing mypackage
>>> mypackage.greet
<function greet at 0x7f61b8f36700>
  1. all参数
    当我们在 __init__.py 中不引入任何包时,调用 from my package import * 是不会引入任何包的,因为此时 mypackage 中不存在任何属性
# mypackage/__init__.py
print("Initializing mypackage")

当我们之引入 module1 时,调用 from my package import * 只会引入 module1,但是调用 module2 就会报错

# mypackage/__init__.py
print("Initializing mypackage")
from . import module1

当引入 module1module2 时,调用 from my package import * 默认会将二者均引入,但是我们可以通过 __all__ 参数来控制其引入的模块

# mypackage/__init__.py
print("Initializing mypackage")
from . import module1
from . import module2
__all__ = ['module1']

此时我们执行上述 python 程序,可以得到如下内容,可以发现 __all__ 参数仅仅用来控制 from mypackage import * 这种星号导入的情况

>>> from mypackage import *
Initializing mypackage
>>> module1
<module 'mypackage.module1' from '/apdcephfs/llm-cfs-nj/person/harryyhwang/test/mypackage/module1.py'>
>>> module2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'module2' is not defined. Did you mean: 'module1'?
4月 06, 2025
3月 10, 2025
12月 31, 2024