从不同目录导入Python模块失败

3

我有这个Python3的代码结构:

- datalake
  __init__.py
  utils
     __init__.py
     utils.py
  lambdas
     __init__.py
     my-lambdas.py
- tests
    __init__.py
    demo.py

所有的init__.py文件都是空的。

我的问题是如何从tests/demo.py导入datalake模块?我尝试在demo.py中使用from datalake.utils import utils,但是当我在命令行中运行python tests/demo.py时,我会得到这个错误:ModuleNotFoundError: No module named 'datalake'

如果我使用这段代码:

from ..datalake.utils import utils

我将会得到错误信息 ValueError: attempted relative import beyond top-level package

我还尝试从 my-lambda.py 文件中导入模块 utils,但是也失败了。在 my-lambda.py 文件中的代码是 from datalake.utils import utils,但是当我从命令行运行 python datalake/lambda/my-lambda.py 时,我会得到 ModuleNotFoundError: No module named 'datalake' 的错误信息。

我该如何导入这个模块呢?


父目录(包含 datalake 和 tests 的目录)是否在你的 PYTHONPATH 中?如果是的话,它应该能够找到该模块。 - Schematic
1
不是这样的。我需要将路径添加到PYTHONPATH环境变量中吗? - Joey Yi Zhao
5个回答

4
当你运行像python tests/demo.py这样的命令时,你所在的文件夹并没有被添加到PYTHONPATH中,而脚本文件夹被添加了。所以像import datalake这样的顶级导入会失败。为了解决这个问题,你可以将测试作为一个模块来运行:
Python 2:
python -m tests/demo

Python 3:

python -m tests.demo

如果您使用的是demo.py文件并且导入了任何datalake,则将正常工作。

听起来您真正想做的是拥有一个与主应用程序分开的测试文件夹并运行它们。针对这种情况,我建议使用py.test,您可以阅读Tests Outside Application Code以了解如何进行操作。简而言之,从项目的顶层文件夹运行python -m py.test即可。


当我运行 python -m tests/demo 时,出现了错误 No module named tests/demo。我需要改变其他什么吗? - Joey Yi Zhao
@ZhaoYi 你在顶级项目文件夹里吗?这样从你当前所在的文件夹中就存在 tests/demo.py 了吗? - bananafish
是的,文件tests/demo.py存在于当前目录中,该目录是项目的根目录。 - Joey Yi Zhao
@ZhaoYi 抱歉,看起来这个语法只适用于Python 2。请改用 python -m tests.demo - bananafish

2
首先,my-lambdas.py 不能使用 import 语句导入,因为 Python 标识符中不允许使用连字符。建议遵循PEP-8 命名规范,例如使用 mylambdas.py

除此之外,包结构看起来很好,只要你在 datalake/ 的上一级目录下,比如在目录 myproject/ 中,就可以导入了。

myproject
├── datalake
│   ├── __init__.py
│   ├── utils
│   │   ├── __init__.py
│   │   └── utils.py
│   └── lambdas
│       ├── __init__.py
│       └── mylambdas.py
└── tests
    ├── __init__.py
    └── demo.py

那么这个应该可以工作:

然后这个应该可以工作:

~/myproject$ python -c 'from datalake import utils'

否则,将环境变量PYTHONPATH设置为上述路径中的datalake/或修改sys.path都是更改Python导入位置的方法。有关更多信息,请参见官方模块教程
另外,一些普遍的建议:在需要扩展之前,我发现坚持使用简单的模块而不是包(目录)很有用。然后,您可以将foo.py更改为一个带有__init__.py文件的foo/目录,import foo将像以前一样工作,尽管您可能需要向__init__.py添加一些导入以维护API兼容性。这将使您拥有更简单的结构:
myproject
├── datalake
│   ├── __init__.py
│   ├── utils.py
│   └── lambdas.py
└── tests
    ├── __init__.py
    └── demo.py

我该如何在命令行中运行 tests/demo.py?如果我当前在 myproject 目录下,运行 python -m tests/demo 会出现 No module named tests/demo 错误。 - Joey Yi Zhao
如果这些是单元测试,单元测试框架通常有一个运行器来发现它们。例如使用 pytest(顺便说一下,这个工具很棒),你只需要在 myproject/ 目录下运行 pytest 命令,它就会自动发现符合命名规范的文件(但是它们应该被命名为类似于 test_lambdas.py 的名称)。如果 demo.py 是你想要交互式运行的东西,请尝试使用 python -m tests.demo 命令。请注意,在模块路径中使用点号 (.) 而不是斜杠 (/)。 - goodmami
它们不是单元测试。它们是演示代码,用于从命令行运行我们的项目。 - Joey Yi Zhao

1
你可以将模块目录添加到你的sys.path中:
import sys
sys.path.append("your/own/modules/folder")  # like sys.path.append("../tests")

但这是一种一次性的方法,只在此时有效,添加的路径不是永久的,在代码执行完毕后会被删除。

0

导入文件的一种方法是直接使用 import util,而不是使用 from


-1

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