【问题标题】:Understanding A Chain of Imports in Python了解 Python 中的导入链
【发布时间】:2011-03-08 00:11:10
【问题描述】:

我知道有几个类似的问题,但我很难理解我遇到的错误,浏览文档和类似的问题还没有帮助。如果有的话,类似的问题让我觉得我所做的是正确的。

我有以下文件:

src/main.py

from pack import pack

if __name__ == '__main__':
    pack.exec("Hello Universe!")

src/pack/pack.py

import util

def exec(text):
    util.write(text)

if __name__ == '__main__':
    exec("Hello World!")

src/pack/util.py

def write(text):
    print(text)

*src/pack/_init_.py*

EMPTY FILE

当我从src/pack 目录运行python pack.py 时,它可以工作(打印“Hello World!”)。但是,当我从 src 目录运行 python main.py 时,我得到以下异常:

Traceback (most recent call last):
  File ".../src/main.py", line 1, in <module>
    from pack import pack
  File ".../src/pack/pack.py", line 1, in <module>
    import util
ImportError: No module named util

如果我按照建议将pack.py 中的导入行更改为from . import util,实际上会发生相反的情况。 main.py 运行成功,但现在 pack.py 失败,引发:

Traceback (most recent call last):
  File ".../src/pack/pack.py", line 1, in <module>
    from . import util
ValueError: Attempted relative import in non-package

我会认为导入是相对于当前位置的,因此您应该能够构建这样的导入链。在我看来,模块应该根据程序的启动位置以不同的方式导入同级文件。

有人可以解释为什么这个错误以一种方式发生而不是另一种,如果有某种方法可以让这个文件结构运行,无论我是想从main.py还是pack.py运行?

【问题讨论】:

    标签: python import python-3.x


    【解决方案1】:

    在这两种情况下,您都将无法进行导入。这是因为在一种情况下,您将 pack.py 作为主文件运行,而在另一种情况下,您将其作为包的一部分运行。

    当您将其作为独立脚本python pack.py 运行时,“pack”目录将添加到 PYTHONPATH,这意味着您可以在其中导入任何模块。因此,import util 将起作用。

    当你运行python main.py 时,你将 src 目录添加到你的 PYTHONPATH 中。这意味着src 中的任何模块或包,例如pack 目录,现在都可以导入了。因此from pack import pack。但是,要访问util.py,您现在需要执行from pack import util。正如您所注意到的,您还可以在 pack.py 中执行 from . import util

    但你不能同时做到这两点。 src/ 是主目录或src/pack 是。

    显而易见但错误的解决方案是让 main.py 将src/pack 目录添加到 PYTHONPATH。这会起作用,但这不是一个好主意。正确的做法是下定决心。 src/pack 是一个应该通过 import pack 导入的模块,还是只是一个包含一堆 Python 脚本的文件夹?决定! :-)

    我认为在这种情况下,src/pack 显然应该是一个模块。所以然后把它当作一个模块,并确保它像一个模块一样可用。然后即使将pack.py 作为主脚本运行,您也可以from pack import util

    你是怎么做到的?好吧,基本上你要么在你的站点包中安装 pack 模块,要么将src 目录添加到 PYTHONPATH。最后一个是你在开发过程中想要的。您可以使用export PYTHONPATH=&lt;path&gt; 手动执行此操作,也可以让您的测试运行人员为您执行此操作。你没有测试员?嗯,你应该,但这是另一个问题。 :)

    要在您不再进行开发后永久安装它,请查看Distribute。它包括一个测试运行器。 ;)

    【讨论】:

    • 您能否详细说明“src/pack 应该是一个模块”是什么意思?你的意思是目录本身应该是一个模块?
    • @dimo414:我的意思是它应该有一个__init__.py。这使它表现得像一个模块。严格来说,作为模块的目录的 python 术语是“包”,但这往往会使包括我在内的人感到困惑。 :)
    • @LennartRegebro:术语确实令人困惑。作为一个试图找到一个好的心智模型的新手,我目前是:“包是一个可以包含子模块的模块”。但是它们仍然是模块:您可以单独导入它们 import pack 并使用它们的对象(在 __init__.py 中)和子模块。 __init__.py 使目录被视为一个包。
    【解决方案2】:

    您的链接指向 python 2.7 文档,但看起来您使用的是 Python 3.x。

    请参阅此处:http://docs.python.org/py3k/ 以获取正确的文档。

    Python 3 移除了隐式相对导入。也就是说,您不能再以相同的方式在同一个模块中导入包。

    您需要使用from . import util,这是一个明确的相对导入并且是允许的。 import X 不再检查当前目录。相反,它只检查 sys.path 中的条目。这包括脚本启动的目录和python的标准库。

    【讨论】:

    • 好的,感谢您向我解释。麻烦的是,现在它不起作用/另一种/方式。运行 main.py 有效,但 pack.py 无效。
    • @dimo414:那是因为你应该使用-m 来运行pack.pack
    • @dimo414,不赞成在包内运行脚本。您要运行的任何脚本都不应该在包中。
    【解决方案3】:

    您的包目录中缺少 __init__.py。

    在你的包目录中创建一个名为 __init__.py 的空文件。

    【讨论】:

    • +1 - __init__.py 文件将告诉 python 该目录是一个包。您可以在__init__.py 文件中指定要加载或不加载的内容以及其他内容,但一个空文件就足够了并且可以工作。
    • 原谅我没提,但是有一个空的__init__.py文件。
    【解决方案4】:

    correct documentation 中没有任何内容支持您的理论,即这种形式的导入应该有效。

    【讨论】:

    • 好的,谢谢,修复了链接。然而,据我所知,我拥有的代码应该仍然可以运行。
    • 我做到了,恕我直言,主题不是很清楚。我的问题的目的是在包中/包外运行文件在我看来是违反直觉的,我试图了解这方面的最佳实践,如果有任何方法可以解决我的具体问题,能够跑进跑出包。
    • 运行包的正确方法是将-m传递给可执行文件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-06
    • 2019-10-28
    相关资源
    最近更新 更多