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

jas*_*rim 7 python import

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

quick
??? brown
?   ??? fox.py
?   ??? __init__.py
??? lazy
?   ??? dog.py
?   ??? __init__.py
??? __init__.py
Run Code Online (Sandbox Code Playgroud)

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

最近看到Raymond Hettinger 在Pycon 2015上演讲我希望这个函数可以直接从包的根目录导入lazy,如下所示:

from lazy import jump 
Run Code Online (Sandbox Code Playgroud)

此外,在我看来,编写相对导入更简洁,并使包内连接容易可见.因此,我将其写入lazy/__init__.py:

from .dog import jump
Run Code Online (Sandbox Code Playgroud)

这进入fox.py:

from ..lazy import jump
Run Code Online (Sandbox Code Playgroud)

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

首先,导入名jumplazy/__init__.py没有采取任何措施防止它直接被导入dog.如果某个功能可能从很多地方导入,会导致问题吗?例如,在单元测试中,我们是否可能从错误的位置修改名称?

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

将包裹外所需的所有名称带到其他地方是否危险__init__.py?可能这至少增加了循环进口的可能性.但我想如果遇到循环导入,无论如何都会对包结构产生根本性的错误.

相对进口怎么样? PEP 8表示建议绝对进口:当它说绝对进口表现优于相对进口时,它意味着什么?能给我举个例子?

小智 4

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

关于防止人员/工具直接从 导入它dog:在Python中,隐私取决于用户的同意,你不能强行隐藏任何东西,但有约定。

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

这是关于这个主题的一个很好的指南: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