Python 复杂包结构中的导入语句?

Python import statements in complex package structures?

考虑以下三个常规包的层次结构及其 内容:

quick
├── brown
│   ├── fox.py
│   └── __init__.py
├── lazy
│   ├── dog.py
│   └── __init__.py
└── __init__.py

现在假设模块 dog 中有一个函数 jump,并且模块 fox 中需要它。我该如何进行?

最近看了 Raymond Hettinger 在 Pycon 的 演讲 2015我愿意 可直接从包 lazy 的根导入的函数, 像这样:

from lazy import jump 

另外,我觉得写relative import更简洁明了 使包内连接很容易看到。因此,我会写 这变成 lazy/__init__.py:

from .dog import jump

然后变成 fox.py:

from ..lazy import jump

但我想知道,这是正确的方法吗?

首先,在 lazy/__init__.py 中导入名称 jump 对 防止它直接从 dog 导入。如果一个函数可能从许多地方导入,它会导致问题吗?例如,在单元测试中,我们是否可以从错误的位置修补名称?

此外,具有自动导入例程的 IDE 似乎更喜欢从定义函数的模块中导入。我也许可以通过将字符 _ 放在所有模块名称的前面来覆盖它,但这似乎有点不切实际。

把所有需要的名字都带在外面是不是很危险? 打包到 __init__.py?可能这至少增加了 循环进口的可能性。但我想如果一个通告 import 遇到了一些根本性的错误 无论如何包结构。

相对进口呢? PEP 8 说 建议绝对进口:绝对进口是什么意思 进口产品表现优于相关产品?你能给我一个 例如?

显式接口声明:如果你想公开属于lazy包的jump函数,那么包含它是有意义的lazy.__init__ ,正如你所建议的。这样你就清楚地表明它是 lazy 的“public 接口 ” 的一部分。您还建议其他模块不是 public 接口的一部分。

关于阻止people/tools直接从dog导入:在Python中,隐私权取决于用户的同意,你不能强行隐藏任何东西,但有约定俗成。

使用下划线和定义 dog._jump() 可以清楚地表明 dog 不想 暴露 _jump。我们可以假设任何 IDE 工具都应该遵守这种约定。好歹如果dog定义了_jump,而lazy暴露了jump,那你就不会有不知道导入哪个的问题,因为名字不一样,所以这是明确的,这在 Python 中被认为是好的。

这里有一个关于这个主题的很好的指示:Defining private module functions in python

关于相对导入::PEP 8 不鼓励这些,但是它们的实现是有原因的,并且它们取代了隐式相对导入。 PEP 8 中的原因:尤其是在处理复杂的包布局时,使用绝对导入会不必要地冗长

最后的想法:简而言之,如果你认为lazy包是一个库并且不想暴露内部模块,那么我认为它公开 lazy.__init__ 中的对象是有意义的。相反,如果你想让人们知道有一个 dog 模块,那么无论如何,让其他模块做:

from lazy.dog import jump

from ..lazy import jump

如果 brownbrown.fox 将始终打包并与 lazy 紧密集成,那么我看不出绝对和相对之间的区别,但我更喜欢相对, 以明确指示您指的是内部模块。

但如果你认为它们将来可以拆分,那么相对导入就没有意义,你宁愿这样做,取决于以上几点:

from lazy.dog import jump
或:
from lazy import jump