在Python包中导入的正确方法

时间:2014-05-13 20:56:09

标签: 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
    /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

添加到我的每个文件似乎很笨拙。

4 个答案:

答案 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)

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