跨Python包导入的正确方法

跨Python包导入的正确方法,python,packaging,Python,Packaging,假设我有两个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

假设我有两个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
使用实用程序包的最佳方式是什么?假设shinny_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

这似乎很难添加到我的每个文件中

我认为最安全的方法是始终使用绝对导入,因此在您的情况下:

from package_name.utilities import important

这样,如果您决定在其他包中移动您的shiny_stuff.py(假设包名仍在您的sys.path中),您就不必更改代码。

我认为最安全的方法是始终使用绝对导入,因此在您的情况下:

from package_name.utilities import important

这样,如果您决定在其他包中移动您的shinny_stuff.py,您就不必更改代码(假设包_名称仍在sys.path中).

如果需要移动到其他位置,请使用绝对导入。

如果需要移动到其他位置,请使用绝对导入。

根据Nick Coghlan(Python核心开发人员)的说法:

(在“双重进口陷阱”标题下)

将包目录添加到路径为模块提供了两种不同的引用方式。上面的链接是一篇关于Python导入系统的优秀博文。直接将其添加到路径意味着您可能拥有一个模块的两个副本,这是您不想要的。从.utilities import import import import important进行相对导入是可以的,而绝对导入
import package\u name.utilities.important
也是可以的。

根据Nick Coghlan(Python核心开发人员)的说法:

(在“双重进口陷阱”标题下)

将包目录添加到路径为模块提供了两种不同的引用方式。上面的链接是一篇关于Python导入系统的优秀博文。直接将其添加到路径意味着您可能拥有一个模块的两个副本,这是您不想要的。从.utilities import import import import important进行相对导入是可以的,而绝对导入
import package\u name.utilities.important
也是可以的。

可能不存在“最佳”上下文外选择,但您可以有一些标准来选择更适合您的用例,对于这种判断,我们应该知道不同的可能方法及其特点。可能最好的信息来源是信息本身,它包含了一些关于声明不同可能性的基本原理

一种常见的方法是使用“绝对导入”,在您的情况下,它类似于:

from package_name.utilities import important
这样,您就可以将此文件设置为脚本。它在某种程度上独立于其他模块和包,主要由其位置固定。如果您有一个包结构,并且需要从其位置更改一个模块,那么拥有绝对路径将有助于保持此单个文件不变,但是使用此模块的所有文件都应该更改。当然,您也可以将
\uuuu init\uuuu.py
文件导入为:

from package_name import utilities
这些进口商品都有相同的特点。请注意,
实用程序.important
尝试在
\uuuu init\uuuuuuuuuuuuuuuuupy
中查找变量
import important.py
,而不是从import important.py中查找变量,因此使用“import important important”
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

另一种方法是相对方法,使用:

from ..utilities import important
第一个点(
from.stuff import
from.import
)表示“此[子]包中的模块”,或当只有点时,表示
\uuu init\uuuuuu.py
。从第二点开始,我们讨论的是父目录。通常,脚本/可执行文件中不允许在任何导入中以点开头,但如果您关心具有相对导入的脚本,则可以阅读

可在PEP 328本身上找到相对导入的理由:

随着向绝对进口的转变,出现了一个问题,即是否应该允许相对进口。介绍了几个用例,其中最重要的是能够重新安排大型软件包的结构,而无需编辑子软件包。此外,如果没有相对导入,包中的模块无法轻松导入自身

无论哪种情况,模块都与子包绑定在一起,即无论用户尝试首先导入哪个包,都会首先导入
包\u name
,除非您使用sys.path搜索子包作为包(即,使用sys.path中的包根)……但这听起来很奇怪,为什么要这样做

\uuuu init\uuuuu.py
可以自动导入模块名称,因为您应该关心其名称空间内容。例如,假设
important.py
有一个名为
top\u secret
的对象,它是一个字典。从你需要的任何地方找到它

from package_name.utilities.important import top_secret
也许您希望不那么具体:

from package_name.utilities import top_secret
这将通过一个
\uuuu init\uuuu.py
完成,其中包含以下行:

from .important import top_secret
这可能混合了相对导入和绝对导入,但是对于
\uuuu init\uuuu.py
来说,您可能知道子包作为子包是有意义的,也就是说,它本身就是一个抽象。如果只是位于同一位置的一组文件,并且需要显式的模块名,那么
\uuuu init\uuuuuuuuuuuy.py
可能是空的(或者几乎是空的)。但是为了避免用户使用显式的模块名,可以在根
\uuuu init\uuuu.py
上使用相同的方法

from .utilities import top_secret
完全是间接的,但是当文件为某些内部组织嵌套时,名称空间以这种方式变得平坦。例如,
wx
包(wxPython)可以这样做:所有东西都可以直接从wx导入中找到

如果需要,还可以使用一些元编程来查找内容