我们说我有几个Python包。
/package_name
__init__.py
/dohickey
__init__.py
stuff.py
other_stuff.py
shiny_stuff.py
/thingamabob
__init__.py
cog_master.py
round_cogs.py
teethless_cogs.py
/utilities
__init__.py
important.py
super_critical_top_secret_cog_blueprints.py
利用utilites包的最佳方式是什么?假设shiny_stuff.py需要导入important.py,那么最好的方法是什么?
目前我正在思考
from .utilities import important
但那是最好的方法吗?将实用程序添加到路径并以这种方式导入它会更有意义吗?
import sys
sys.path.append(os.path.basename(os.path.basename(__file__)))
import utilities.super_critical_top_secret_cog_blueprints
添加到我的每个文件似乎很笨拙。
答案 0 :(得分:3)
我认为最安全的方法总是使用绝对导入,所以在你的情况下:
from package_name.utilities import important
这样,如果您决定将shiny_stuff.py移动到其他软件包中,则不必更改代码(假设package_name仍在您的sys.path中)。
答案 1 :(得分:2)
根据Nick Coghlan(Python核心开发人员)的说法:
"“Never add a package directory, or any directory inside a package, directly to the Python path.”(标题为"双重导入陷阱")
将包目录添加到路径为模块提供了两种不同的方式。上面的链接是关于Python导入系统的优秀博客文章。将其直接添加到路径意味着您可能有两个单个模块的副本,您不想要的。您的相对导入from .utilities import important
没问题,绝对导入import package_name.utilities.important
也没问题。
答案 2 :(得分:1)
可能不存在“最佳”的上下文选择,但您可以选择哪一个更适合您的用例的标准,并且对于这样的判断,应该知道不同的可能方法及其特征。可能最好的信息来源是PEP 328本身,其中包含了为此宣布不同可能性的一些基本原理。
一种常见的方法是使用“绝对导入”,在您的情况下,它将类似于:
from package_name.utilities import important
这样,您可以将此文件设为脚本。它有点独立于其他模块和包,主要由其位置修复。如果你有一个包结构并且需要从它的位置改变一个单独的模块,那么拥有绝对路径将有助于保持这个单个文件不变,但是所有使用该模块的文件都应该改变。当然,您也可以将__init__.py
文件导入为:
from package_name import utilities
这些进口具有相同的特征。请注意utilities.important
尝试在important
内找到变量__init__.py
,而不是来自important.py,因此拥有“导入重要”__init__.py
有助于避免由于文件结构和命名空间结构之间的区别。
另一种方法是使用相对方法:
from ..utilities import important
第一个点(from .stuff import ___
或from . import ___
)表示“此[sub]包中的模块”,或者__init__.py
表示只有点。从第二个点我们谈论父目录。通常,在脚本/可执行文件中不允许以任何导入中的点开头,但如果您关心具有相对导入的脚本,则可以阅读explicit relative imports (PEP 366)。
可以在PEP 328本身找到相对导入的理由:
随着向绝对进口的转变,问题出现了是否应该允许相对进口。提出了几个用例,其中最重要的是能够重新排列大包的结构,而无需编辑子包。另外,如果没有相对导入,包内的模块就不能轻易导入。
无论哪种情况,模块都绑定到子包,意思是首先导入package_name
,无论用户首先尝试导入哪个,除非您使用sys.path搜索子包作为包(即,使用sys.path中的包根... ...但这听起来很奇怪,为什么会这样做呢?
__init__.py
可以自动导入模块名称,因为它应该关心它的名称空间内容。例如,假设important.py
有一个名为top_secret
的对象,它是一个字典。要从任何需要的地方找到它
from package_name.utilities.important import top_secret
也许你想要不那么具体:
from package_name.utilities import top_secret
这将使用__init__.py
,其中包含以下行:
from .important import top_secret
这可能混合了相对和绝对导入,但是对于__init__.py
,您可能知道子包作为子包是有意义的,即作为抽象本身。如果它只是位于相同位置的一堆文件,需要显式模块名称,那么__init__.py
可能是空的(或几乎为空)。但是为了避免用户的显式模块名称,可以在根__init__.py
上使用
from .utilities import top_secret
完全间接,但命名空间以这种方式变得扁平,而文件嵌套在一些内部组织中。例如,wx
包(wxPython)可以做到:直接找到所有内容from wx import ___
。
如果要使用此方法,也可以使用某些元编程来查找内容,例如,使用__all__
检测模块的所有名称,或查找文件位置以了解哪些模块/子包可以在那里导入。但是,一些更简单的代码完成实用程序可能会在执行此操作时丢失。
对于某些情境,您可能会遇到其他类型的限制。例如,macropy使用导入产生一些“魔力”,并且对您作为脚本调用的文件不起作用,因此您只需要至少2个模块来使用此包。
无论如何,您应该总是询问代码或API组织是否真的需要嵌套到子包中。 PEP 20告诉我们“Flat优于嵌套”,这不是法律,而是一种观点,表明你应该保持扁平的包结构,除非出于某种原因需要嵌套。同样,您不需要每个类的模块也不需要任何类似的模块。
答案 3 :(得分:0)
如果您需要移动到其他位置,请使用绝对导入。