Python找不到模块

12

考虑以下在PyDev中创建的Python项目:

├── algorithms
│   ├── __init__.py
│   └── neighborhood
│       ├── __init__.py
│       ├── neighbor
│       │   ├── connector.py
│       │   ├── __init__.py
│       │   ├── manager.py
│       │   └── references.py
│       ├── neighborhood.py
│       ├── tests
│       │   ├── fixtures
│       │   │   └── neighborhood
│       │   ├── __init__.py
│       └── web
│           ├── __init__.py
│           └── service.py
├── configuration
│   ├── Config.py
│   └── __init__.py
├── __init__.py
└── webtrack
    |- teste.py
    ├── .gitignore
    ├── __init__.py
    ├── manager
        ├── Data.py
        ├── ImportFile.py
        └── __init__.py

我们一直在尝试将模块从一个文件夹导入到另一个文件夹,但一直没有成功,例如:

from algorithms.neighborhood.neighbor.connector import NeighborhoodConnector

这将产生结果:

Traceback (most recent call last):
File "teste.py", line 49, in <module>
from algorithms.neighborhood.neighbor.connector import NeighborhoodConnector
ImportError: No module named algorithms.neighborhood.neighbor.connector

我们尝试将其路径添加到sys.path变量中,但没有成功。

我们还尝试使用os.walk将所有路径插入PATH变量中,但仍然出现相同的错误,尽管我们检查了PATH是否包含查找模块的路径。

我们正在使用Python 2.7在Linux Ubuntu 13.10上运行。

我们可能做错了什么吗?

提前致谢!


你已经验证了问题在导入链的哪个位置出现了吗?也就是说,import algorithms.neighborhood.neighbor.connector 失败了吗?import algorithms.neighborhood.neighborimport algorithms.neighborhoodimport algorithms 呢? - BrenBarn
@BrenBarn 我们尝试了你建议的方法,但是仍然出现了相同的错误。 - Willian Fuks
2
你的项目树中“teste.py”在哪里? - dyoo
1
你的项目的最顶层目录(即你所展示的无名顶层包之上的级别),是否在sys.path中?如果是,那么你应该能够使用绝对导入,如:from your_package.algorithms.neighborhood.neighbor.connector import NeighborhoodConnector。或者,如果你正确执行了脚本(使用正确设置的sys.path-m,或者使用从顶层文件夹开始的相对路径),你应该能够使用显式的相对导入:from ..algorithms.neighborhood.neighbor.connector import NeighborhoodConnector - Blckknght
将我们的上级文件夹(名为recsys)插入到sys.path中并尝试进行绝对导入会产生错误: /usr/bin/python: No module named recsys.algorithms.neighborhood.neighbor.connector 不确定为什么它打印了“/usr/bin/python”,但它没有起作用。 - Willian Fuks
显示剩余4条评论
4个回答

16

在运行位于包中的脚本时,正确处理导入很棘手。您可以阅读(遗憾的是未完成的)PEP 395的本节,描述了许多行不通的方式来运行这样的脚本。

给出一个文件系统层次结构如下:

top_level/
    my_package/
        __init__.py
        sub_package/
            __init__.py
            module_a.py
            module_b.py
            sub_sub_package/
                __init__.py
                module_c.py
        scripts/
            __init__.py
            my_script.py
            script_subpackage/
                 __init__.py
                 script_module.py

my_script.py 运行正确只有几种方法。

  1. 第一种方法是将 top_level 文件夹放入 PYTHONPATH 环境变量中,或使用一个 .pth 文件来实现同样的功能。或者,在解释器运行时,将该文件夹插入到 sys.path 中(但这可能会很麻烦)。

    请注意,您要将 top_level 添加到路径中,而不是 my_package!我怀疑您在当前尝试解决此问题时搞错了这个。它非常容易出错。

    然后,像 import my_package.sub_package.module_a 这样的绝对导入通常会正常工作。(仅在以 __main__ 模块运行时尝试导入 package.scripts.my_script 本身时,您将获得一个奇怪的重复模块副本。)

    然而,相对导入总是比绝对导入更冗长,因为您总是需要指定完整路径,即使您导入的是一个兄弟模块(或像 module_cmodule_a 这样的“侄女”模块)。使用绝对导入,无论进行导入的模块是什么,获取 module_c 的方法始终是冗长且笨拙的代码 from my_package.sub_package.sub_sub_package import module_c

  2. 出于这个原因,相对导入通常更加优雅。但遗憾的是,它们很难从脚本中工作。唯一的方法是:

    1. 使用 -m 标志(例如 python -m my_package.scripts.my_script)从 top_level 文件夹运行 my_script,而不是通过文件名。

      如果您在不同的文件夹中或者使用不同的方法运行脚本(例如在 IDE 中按 F5 键),则它将无法正常工作。这有点不灵活,但实际上没有简化它的方法(直到 PEP 395 被取消推迟并实施为止)。

    2. 像绝对导入一样设置 sys.path(例如将 top_level 添加到 PYTHONPATH 或其他地方),然后使用 PEP 366__package__ 字符串告诉 Python 您的脚本的预期包。也就是说,在 my_script.py 中,您需要在所有相对导入之前放置类似于以下内容的代码:

      if __name__ == "__main__" and __package__ is None:
          __package__ = "my_package.my_scripts"
      

      如果您重新组织文件结构并将脚本移动到不同的包中,这将需要更新(但这可能比更新大量绝对导入更容易)。

    一旦您实现了其中一种解决方案,您的导入可以变得更简单。从module_a导入module_c变成了from .sub_sub_package import module_c。在my_script中,像from..subpackage import module_a这样的相对导入将可以正常工作。


谢谢Blcknght!您能解释一下为什么要将top_level/而不是my_package/设置为PYTHONPATH吗?是越高越好还是只需在整个包的顶部再上一级? - Steven
1
它只是真正包的上一级。当你执行 import my_package 时,Python 将检查 PYTHONPATH 中的每个文件夹,以查看是否包含名为 my_package.py 的文件或名为 my_package 的文件夹,并且其中有一个 __init__.py 文件。 - Blckknght
以模块方式运行,永远不要通过文件名运行。这对我很有效。 - Catiger3331

2

我知道这是一个旧帖子,但我仍然会发布我的解决方案。

我遇到了类似的问题。只需在导入包之前添加以下行中的路径:

sys.path.append(os.path.join(os.path.dirname(__file__), '..')) 
from lib import create_graph

1
在每个.py文件中设置__package__ = None,即可自动设置所有包层次结构。之后,您可以自由地使用绝对模块名称进行导入。
from algorithms.neighborhood.neighbor.connector import NeighborhoodConnector

1
Python 2和3的导入方式略有不同。首先是Python 3的做法(你似乎期望的那样)。在Python 3中,所有的导入都是相对于sys.path文件夹的(有关模块搜索路径的更多信息,请在此处查看)。顺便说一下,Python不使用$PATH

因此,您可以从任何地方导入任何内容,而不必过多担心。

在Python 2中,导入有时是相对的,有时是绝对的。有关软件包的文档包含一个示例布局和一些可能对您有用的导入语句。

部分“软件包内引用”包含有关如何在软件包之间进行导入的信息。

从以上所有内容来看,我认为你的sys.path是错误的。确保包含algorithms的文件夹(即不是algorithms本身而是它的父文件夹)需要在sys.path中。

尝试将from ..algorithms.neighborhood.neighbor.connector import NeighborhoodConnector这一行插入到teste.py中,但出现了以下错误: ValueError: Attempted relative import in non-package. 是否有做错什么?所有__init__.py文件都存在。 - Willian Fuks
这真的很奇怪。1. 你可以使用 python -v 调试正常工作的导入,但不能调试失败的导入。无论如何都要尝试一下。2. 或许在某个 __init__.py 文件中有错别字?3. 再次检查 sys.path 是否包含正确的值。或许可以尝试手动加载模块:http://docs.python.org/2/library/importlib.html#importlib.import_module - Aaron Digulla

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接