复杂包结构中的 Python 导入语句?
Posted
技术标签:
【中文标题】复杂包结构中的 Python 导入语句?【英文标题】:Python import statements in complex package structures? 【发布时间】:2015-07-26 04:42:10 【问题描述】:考虑以下三个常规包的层次结构及其 内容:
quick
├── brown
│ ├── fox.py
│ └── __init__.py
├── lazy
│ ├── dog.py
│ └── __init__.py
└── __init__.py
现在假设在模块dog
中有一个函数jump
,并且在模块fox
中需要它。我应该如何进行?
最近看过 Raymond Hettinger 的 talk at Pycon
2015 我会喜欢
可以从包lazy
的根目录直接导入的函数,
像这样:
from lazy import jump
另外,在我看来,写相对导入更简洁,
使封装内的连接很容易看到。因此,我会写
这变成lazy/__init__.py
:
from .dog import jump
这变成fox.py
:
from ..lazy import jump
但我想知道,这是正确的方法吗?
首先,在 lazy/__init__.py
中导入名称 jump
不会
防止它直接从dog
导入。如果一个函数可能从许多地方导入,它会导致问题吗?例如,在单元测试中,我们可以猴子修补错误位置的名称吗?
此外,带有自动导入例程的 IDE 似乎更喜欢从定义函数的模块中导入。我也许可以通过将字符 _
放在所有模块名称前面来覆盖它,但这似乎有点不切实际。
将所有需要的名称带到外部是否有危险?
打包到__init__.py
?可能这至少增加了
循环进口的可能性。但我想如果一个圆形
遇到 import 有一些根本性的错误
无论如何都是包结构。
相对进口呢? PEP 8 说 推荐绝对进口:当它说绝对是什么意思 进口的表现比相对的好?你能给我一个 例子?
【问题讨论】:
您确定jump
属于dog.py
,而不是更高层级吗?
是的,我认为lazy
是某种连贯的组件集合,其中一些在quick
的其他地方也需要。
我建议明确一点,尽可能简单。随着时间的推移,包内导入会变得有点疯狂。所以忘记所有__init__.py
魔法吧。如果你真的想要,你可以只在***__init__.py
中为外部用户可见名称执行此操作。
【参考方案1】:
显式接口声明:如果您想将 jump
函数公开为属于 lazy
包,那么按照您的建议包含它是有意义的 lazy.__init__
。这样您就可以清楚地表明它是lazy
的“公共接口”的一部分。您还建议其他模块不是公共接口的一部分。
关于防止人/工具直接从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
如果brown
和brown.fox
将始终被打包并与lazy
紧密集成,那么我看不出绝对和相对之间的区别,但我更喜欢相对,以明确表明您指的是到一个内部模块。
但是如果您认为它们将来可能会被拆分,那么相对导入没有意义,您宁愿这样做,具体取决于上述几点:
from lazy.dog import jump
或:from lazy import jump
【讨论】:
这是一个组织良好的答案。它很好地澄清了我的一些担忧,但我真的看不出有明显的方法来做到这一点(正如The Zen of Python 所建议的那样)。 另外,我认为将jump
之类的函数定义为_jump
之类的内部函数有点笨拙,因为我必须将几乎所有内容都定义为内部函数。但正如你所说,有一点是有道理的,因为那样名字就会不同。
我猜在 Python 的禅宗意义上没有“明显”的方式,因为 Python 支持这两种方式。在这里,他们有点混淆了这个原则。我建议您查看几个标准包以了解最佳实践。例如,xml包利用了相对导入(仅限于from .xmlreader import InputSource
等一个点),并且还包括一些类到包的__init__.py
文件中,并用_
定义变量和类来标记它们私人的。大多数其他标准包不这样做,并且有一个空的__init__.py
。以上是关于复杂包结构中的 Python 导入语句?的主要内容,如果未能解决你的问题,请参考以下文章