考虑以下三个常规包及其内容的层次结构:
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)
但我想知道,这是正确的方法吗?
首先,导入名jump中lazy/__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
如果brown和brown.fox将始终被打包并紧密集成,lazy那么我看不出绝对和相对之间的区别,但我稍微更喜欢相对,以明确表明您指的是内部模块。
但是,如果您认为它们将来可能会被拆分,那么相对导入就没有意义,您宁愿这样做,具体取决于上述几点:
from lazy.dog import jump
或者:
from lazy import jump