__init__.py的作用

__init__.py的作用

本文转自:https://www.cnblogs.com/tp1226/p/8453854.html
有稍许修改和个人理解。

1 标识该目录是python的包(module package)

  1. 如果在代码中import一个目录,则该目录必须是一个包。
  2. 如果使用python相关的IDE进行开发,如果目录中存在该文件,该目录会被识别为module package

2 简化模块的导入操作

假设我们的模块包的目录结构如下:

.
└── mypackage
    ├── subpackage_1
    │   ├── test11.py
    │   └── test12.py
    ├── subpackage_2
    │   ├── test21.py
    │   └── test22.py
    └── subpackage_3
        ├── test31.py
        └── test32.py

第一行的.表示当前工作目录,我们所有需要import mypackage的文件都在这个目录下,下文所涉及到的所有命令界面也都是这个工作目录。

我们可以选择直接的导入方式,将整个文件拷贝到工程目录下,然后直接导入:

from mypackage.subpackage_1 import test11
from mypackage.subpackage_1 import test12
from mypackage.subpackage_2 import test21
from mypackage.subpackage_2 import test22
from mypackage.subpackage_3 import test31
from mypackage.subpackage_3 import test32

当然这个例子里面的文件比较少,如果模块比较大,目录比较深的话,可能自己都记不清该如何导入。(很有可能,哪怕只想导入一个模块都要在目录中找很久)
这种情况下,__init__.py就派上用场了。下面我们来看看它是如何工作的。

3 __init__.py的工作方式

mypackage文件夹下增加一个__init__.py文件,然后import mypackage的话,会自动执行__init__.py里面的代码。
下面是一个实验,先在mypackage下新建一个__init__.py:

.
└── mypackage
    ├── __init__.py
    ├── subpackage_1
    │   ├── test11.py
    │   └── test12.py
    ├── subpackage_2
    │   ├── test21.py
    │   └── test22.py
    └── subpackage_3
        ├── test31.py
        └── test32.py

./mypackage/__init__.py里面的代码如下:

print("You have imported mypackage")

下面直接在交互模式下import mypackage

>>> import mypackage
You have imported mypackage

可以看出,__init__.py被执行。

4 __init__.py控制模块导入

再做一个实验,我们导入mypackage下的subpackage_1/test11,由于subpackage__init__.py在同一文件夹下,按惯性思维,我们应该在__init__.py中添加以下语句:

from subpackage_1 import test11

我们import mypackage试一试:

>>> import mypackage
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/taopeng/Workspace/Test/mypackage/__init__.py", line 2, in <module>
    from subpackage_1 import test11
ImportError: No module named 'subpackage_1'

报错了我,提示没有subpackage_1模块,这是为什么?
原来,我们在import mypackage时的工作目录是.,不是__init__.py所在的目录,而import是按工作目录寻找模块的,所以__init__.pyimport时应该按照工作目录,故将上一句改为:

from mypackage.subpackage_1 import test11

综上,我们可以利用__init__.py文件指定import mypackage时要导入的模块。

5 __all__的用法

上面讲的都是import mypackage的用法。我们知道在导入时可以直接导入包里面的所有内容,即用下面这行代码代替import mypackage

from mypackage import *

这时就需要__all__这个特殊变量了。
__all__关联了一个存储了模块名称的列表,当执行from xx import *时,就会自动导入列表中的模块。

实验1 :
我们将__init__.py修改为:

__all__ = ['subpackage_1', 'subpackage_2'] # 没有包含subpackage_3,是为了证明__all__起了作用,而不是导入了所有mypackage的子目录

然后再在交互界面执行:

>>> from mypackage import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'subpackage_1', 'subpackage_2']
>>> dir(subpackage_1)
['__doc__', '__loader__', '__name__', '__package__', '__path__', '__spec__']
>>> dir(subpackage_2)
['__doc__', '__loader__', '__name__', '__package__', '__path__', '__spec__']

可以看到subpackage_3没有被导入。但是subpackage_1subpackage_2中的模块也没有被导入!
那么上述导入就相当于执行了:

from mypackage import subpackage_1, subpackage_2

所以导入操作会继续查找subpackage_1subpackage_2中的__init__.py文件并执行(而不是执行import *)。

实验2 :
我们在subpackage_1中新建__init__.py文件:

.
└── mypackage
    ├── __init__.py
    ├── subpackage_1
    │   ├── __init__.py
    │   ├── test11.py
    │   └── test12.py
    ├── subpackage_2
    │   ├── test21.py
    │   └── test22.py
    └── subpackage_3
        ├── test31.py
        └── test32.py

mypackage/subpackage/__init__.py中添加一下代码:

__all__ = ['test11', 'test12']

# 默认情况下只导入test11
from mypacakge.subpackage_1 import test11

再来导入试试:

>>> from mypackage import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'subpackage_1', 'subpackage_2']
>>> dir(subpackage_1)
['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'test11']
>>> dir(subpackage_2)
['__doc__', '__loader__', '__name__', '__package__', '__path__', '__spec__']

如果想要将test11test12都导入,则需要更精确的指定:

>>> from mypackage.subpackage_1 import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'test11', 'test12']

猜你喜欢

转载自blog.csdn.net/qyhaill/article/details/102629538