python 的模块层次结构是否与目录结构相关?

Is python's module hierachy tied to directory structure?

将此视为 this question 的更精致版本。

似乎 python 的模块结构与包含文件的实际目录相关联。虽然可以使用 __init__.py 重新导出另一个模块,但就导入工具而言,这不会改变实际的模块层次结构作为日志。

例如:

some_dir
├ main.py
└ mod_a
  ├ __init__.py
  └ mod_b
    └ mod_c.py

mod_a/__init__.py:

from .mod_b import mod_c

mod_a/mod_b/mod_c.py:

def foo():
    print("Foo!")

在这种情况下,我们可以在 main.py:

from mod_a import mod_c

mod_c.foo()

但不是这个:

import mod_a.mod_c

mod_c.foo()

失败:

Traceback (most recent call last):
  File "main.py", line 1, in <module>
    import mod_a.mod_c
ModuleNotFoundError: No module named 'mod_a.mod_c'

所以 init.py 不能完全改变模块层次结构;这是我所知道的 python 中最接近 'altering module hierachy' 的东西。

那么,有没有办法改变模块层次结构?如:

这可以通过修改 __path__pkgutil.extend_path 或其他方式来完成。

根据 documentation:

A package’s __path__ attribute is used during imports of its subpackages. Within the import machinery, it functions much the same as sys.path, i.e. providing a list of locations to search for modules during import.

__path__ 可以在模块的 __init__.py 中修改,以更改导入系统搜索子模块的位置。

在给定的情况下,使用 pkgutil.extend_path:

# mod_a/__init__.py
from pkgutil import extend_path

__path__ = extend_path(__path__, __name__ + '.mod_b')

或手动修改__path__

# mod_a/__init__.py
__path__.append("(...)/some_dir/mod_a/mod_b")

其中 mod_b 的实际路径最好从 __file__ 计算得出。

These 所以问答涵盖了更多关于使用 __path__.