Python _init_.py 文件
_init_.py主要作用:
- Python文件结构中用来识别软件包(package)的标志,如果其他文件中有对这个目录下文件中类的调用,则不能删除
- 可以通过定义__all__用于模糊导入
_init_.py用于import目录中的类
通常_init_.py文件中我们不写入任何字符,只需要将文件放入对应软件包所在目录,即可对该文件中的类对象进行调用。
├── feudal_networks
│ ├── __init__.py
│ ├── __pycache__
│ │ └── __init__.cpython-37.pyc
│ ├── algos
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-37.pyc
│ │ │ └── policy_optimizer.cpython-37.pyc
│ │ ├── feudal_policy_optimizer.py
│ │ └── policy_optimizer.py
│ ├── envs
│ │ ├── __init__.py
│ │ ├── debug_envs.py
│ │ └── vision_maze.py
│ ├── models
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-37.pyc
│ │ │ └── models.cpython-37.pyc
│ │ └── models.py
│ └── policies
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-37.pyc
│ │ └── lstm_policy.cpython-37.pyc
│ ├── configs
│ │ ├── __init__.py
│ │ ├── feudal_config.py
│ │ └── lstm_config.py
│ ├── feudal_batch_processor.py
│ ├── feudal_policy.py
│ ├── lstm_policy.py
│ ├── policy.py
│ └── policy_utils.py
_init_.py用于控制模块导入
我们想对algos目录下的policy_optimizer.py和feudal_policy_optimizer.py文件在import的时候将名称替换为更容易记住的名称,则需要在feudal_networks目录下的_init_.py文件中添加如下语句:
from feudal_networks.algos import policy_optimizer as poz
from feudal_networks.algos import feudal_policy_optimizer as fpo
# 调用
>>> from feudal_networks import poz
# 调用没有问题
为什么这里不能直接使用algos目录下做相对import?
from algos import policy_optimizer as poz
from algos import feudal_policy_optimizer as fpo
# 如果这样调用则会报错
>>> import feudal_networks
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/scotty/Desktop/feudal_networks-master/feudal_networks/__init__.py", line 1, in <module>
from algos import policy_optimizer as poz
ModuleNotFoundError: No module named 'algos'
这里是因为在我们执行import时,当前目录的相对路径是会改变的,所以我们在_init_.py文件中import需要使用完整的包名。
__all__模糊导入
如果对feudal_networks目录下的_init_.py文件增加如下内容:
__all__ = ["models"] # 只导入models模块,不处理algos和policies模块
# 调试
# 当我们从feudal_networks导入所有包时,可以看到
>>> from feudal_networks import *
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'models']
>>> dir(models)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']
可以看到在总的类目录中可以看到models包,但当我们查看models包内的内容时,会发现models目录下的模块没有被导入(子模块内容未被导入)。
这就需要查看官方文档中关于从包中导入的描述了:
The only solution is for the package author to provide an explicit index of the package. The import statement uses the following convention: if a package’s __init__.py code defines a list named __all__, it is taken to be the list of module names that should be imported when from package import * is encountered. It is up to the package author to keep this list up-to-date when a new version of the package is released. Package authors may also decide not to support it, if they don’t see a use for importing * from their package. For example, the file sound/effects/__init__.py could contain the following code:
__all__ = ["echo", "surround", "reverse"]
This would mean that from sound.effects import * would import the three named submodules of the sound package.
If __all__ is not defined, the statement from sound.effects import * does not import all submodules from the package sound.effects into the current namespace; it only ensures that the package sound.effects has been imported (possibly running any initialization code in __init__.py) and then imports whatever names are defined in the package. This includes any names defined (and submodules explicitly loaded) by __init__.py. It also includes any submodules of the package that were explicitly loaded by previous import statements. Consider this code:
如果在models目录下的_init_.py对all不定义,并且不显式导入子模块,则默认不会将models模块下的任意子块导入到当前的命名空间中,这也是为什么models模块下为空的问题。
正确的导入方法是在models模块下init文件中写入需要导入的包:
__all__ = ['models']
from feudal_networks.models import models
# 调试
>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'models']
>>> dir(models)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'models']
则可以正确的导入子模块。
Reference: