复杂包结构中的python import语句?

Python import statements in complex package structures?

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

1
2
3
4
5
6
7
8
quick
├── brown
│   ├── fox.py
│   └── __init__.py
├── lazy
│   ├── dog.py
│   └── __init__.py
└── __init__.py

现在假设在模块dog中有一个函数jump,在模块fox中需要它。我该怎么办?

最近看了雷蒙德·赫廷格在皮孔的演讲2015我想要直接从包lazy的根目录导入的函数,这样地:

1
from lazy import jump

而且,在我看来,写相关的导入内容更简洁和使包内连接容易可见。因此,我会写这进入了lazy/__init__.py

1
from .dog import jump

这就进入了fox.py

1
from ..lazy import jump

但我想知道,这条路对吗?

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

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

将所有需要的名称带到到__init__.py的包裹?可能这至少增加了循环进口的可能性。但我想如果一个圆遇到导入时,存在根本错误无论如何,包结构。

相对进口呢?PEP 8说建议绝对进口:当它说绝对进口意味着什么?进口比相对进口表现更好?你能给我一个例子?


显式接口声明:如果您希望公开属于lazy包的jump函数,那么按照您的建议,将它包括在lazy.__init__中是有意义的。这样,您就清楚地表明它是lazy的"公共接口"的一部分。您还建议其他模块不是公共接口的一部分。

关于防止人/工具直接从dog导入:在python中,隐私权取决于用户的同意,您不能强制隐藏任何内容,但有一些约定。

使用下划线和定义dog._jump()明确表示dog不想公开_jump。我们可以假设任何IDE工具都应该遵守这种类型的约定。无论如何,如果dog定义了_jump,而lazy公开了jump,那么您就不会有不知道导入哪个的问题,因为名称不同,所以这是明确的,这在python中被认为是好的。

这里有一个关于这个主题的很好的指针:在Python中定义私有模块函数

关于相对进口:PEP8不鼓励这些进口,但它们是出于某种原因实施的,它们取代了隐含的相对进口。PEP8中的原因:特别是在处理复杂的包布局时,使用绝对导入将是不必要的冗长。

最后一点:简而言之,如果您认为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