处理这个问题的正确方法是将需要相互了解的所有内容放在一个共享包下,然后可以通过该包访问各个子包和子模块。但这也需要将应用程序的入口点移动到包或目录中包的同级模块并且可以导入它。如果移动入口点是一个问题,或者原型设计需要快速而肮脏的东西,Python 还实现了一些其他方法来影响导入在哪里搜索可以在答案末尾附近找到的模块。
对于包方法,假设您有这样的结构并且想要在两个模块之间导入一些东西:
.
├── bar_dir
│ └── bar.py
└── foo_dir
└── foo.py
目前,这两个包不知道彼此,因为 Python 只将入口点文件的父级(bar_dir 或 foo_dir 取决于您运行的文件)添加到导入搜索路径,所以我们必须告诉他们以某种方式相互了解,这是通过他们将共享的顶级包完成的。
.
└── top_level
├── __init__.py
├── bar_dir
│ ├── __init__.py
│ └── bar.py
└── foo_dir
├── __init__.py
└── foo.py
这是我们需要的包布局,但是为了能够在导入中使用包,它必须被初始化,这意味着我们要么需要导入 top_level 包(例如从 main.py 入口点),或通过在其中实现__init__.py 旁边的__main__.py 文件将其定义为可运行模块,该文件在执行python -m top_level 时运行。
然后,此入口点模块将运行您之前运行的模块。
__init__.py 文件用于将目录标记为正确的包,并在导入包时运行以初始化其命名空间。
完成此操作后,包现在可以互相看到,并且可以通过绝对或相对导入访问,绝对导入将使用 top_level 包并使用我们需要导入的模块/包的整个虚线路径,例如from top_level.bar_dir import bar 可用于导入bar。
包还允许relative imports,这是一种特殊形式的 from 样式导入,以一个或多个点开头,其中每个点表示导入向上一个包 - 来自 foo 模块 from . import module 会尝试要从foo_dir 包中导入module,from .. import module 将在top_level 包等中搜索它。
需要注意的一点是,导入一个包不会初始化它下面的模块,除非它是该模块的显式导入,例如仅导入 top_level 不会使 foo_dir 和 bar_dir 在其命名空间,除非它们是通过 import top_level.foo_dir/top_level.bar_dir 直接导入的,或者包的 __init__.py 通过自己的导入将它们添加到包的命名空间中。
如果这在您的结构中不起作用,另一种方法是通过添加到其模块搜索路径让 Python 知道在哪里搜索您的模块,这可以在运行时通过将路径字符串插入到 @987654323 中来完成@list,或者通过PYTHONPATH环境变量。
继续上面的例子,从foo导入bar,可以将bar_dir目录(或上面的目录)的条目添加到sys.path列表或上述环境变量中.之后import bar(或from bar_dir import bar,如果添加了父级)可用于导入模块,就像它们彼此相邻一样。插入的路径也可以是相对的,但是随着 cwd 的变化很容易损坏。