使用 sys.path.append() 时出现 ModuleNotFoundError: No module named Project 错误。

5

我正在尝试从父目录中的文件夹中导入模型。 我正在使用sys.path.append()。我的项目结构如下:

- 项目

  • 文件夹1
    • 文件1.py
    • ...
  • 文件夹2
    • 文件2.py
    • ...

在文件1.py中:

sys.path.append('../Project')
from Project.folder2 import file2

我随后得到一个:
ModuleNotFoundError: No module named Project

我知道还有其他方法,但这似乎是最简单的。我不确定是否需要将绝对路径放入项目文件夹中,但我希望不需要,因为我将在不同计算机上运行此项目(使用不同的绝对路径)。


2
你的项目目录中是否有一个名为__init__.py的文件? - ewokx
1
@ewong 我不知道。我在某个地方读到,Python3 不再需要 init 文件,但你有什么想法? - chadlei
@joanis,确切的追加语句是什么?“..”无效。 - chadlei
我刚刚用一个更简单的设置测试了 sys.path.append('..'),并且它对我起作用了。 - joanis
我刚刚看了Niel的回答,无论如何这都是个坏主意,因为路径将会相对于你的进程运行的位置而不是相对于你的文件所在位置。 - joanis
显示剩余5条评论
3个回答

6

您的代码中有2个错误:

  1. Project目录不仅仅是在1级目录上。从file1.py的角度来看,实际上是在2级目录上。请参考以下内容:
$ cd ..
(venv) nponcian 1$ tree
.
└── Project
    ├── folder1
    │   └── file1.py
    └── folder2
        └── file2.py
(venv) nponcian 1$ cd Project/folder1/
(venv) nponcian folder1$ ls ..
folder1  folder2
(venv) nponcian folder1$ ls ../..
Project

即使上述方法有效,添加一个相对路径字符串也只会将该字符串作为其原始值直接附加。因此,如果您添加一个print(sys.path),它会显示类似于以下内容:
['/usr/lib/python38.zip', '/usr/lib/python3.8', '/usr/lib/python3.8/lib-dynload', '../Project', '.']
  • 它实际上添加了'../Project',因此如果Python在folder2中开始搜索目标模块,它仍然找不到它们,因为它怎么知道'../Project'的确切位置。

解决方案

您需要添加的是绝对路径。如果问题是它可能会更改,那没关系,因为我们不需要一个固定的绝对路径。我们可以通过正在执行的当前文件的位置(例如file1.py)获取绝对路径,然后提取所需的父目录。因此,无论绝对路径如何更改,这都将起作用,因为我们获取它的方式始终相对于file1.py。试试这个:

Project/folder1/file1.py

from pathlib import Path
import sys
sys.path.append(str(Path(__file__).parent.parent.parent))  # 1. <.parent> contains this file1.py 2. <.parent.parent> contains folder1 3. <.parent.parent.parent> contains Project

... the rest of the file

就像你最初计划使用 ../../..<etc.> 一样,你只需要继续链接 .parent.parent.parent.<etc.> 直到相对于执行文件达到目标父目录。希望你已经解决了它 :) - Niel Godfrey Pablo Ponciano
1
嘿,那是一个非常酷的工具,“tree”! - joanis
@NielGodfreyPonciano,你能帮我解决一个类似的问题吗?我有一个文件(文件A)在父目录中,它从与A相同目录下的文件夹中导入文件B。文件B从它所在的目录中导入一个文件(文件C)。如果我直接运行文件B,它可以正常运行。但是如果我运行文件A,文件B会报错:ModuleNotFoundError: No module named 'File C'。我该怎么办呢? - chadlei
@chadlei 或许你在将 C 导入 B 时执行了隐式相对导入,这就是为什么当你将当前路径更改为外部目录时它无法工作的原因。如果确实是隐式相对导入,请尝试将其更改为显式 from .C import something(注意 .C 开头)。当然,也可以使用绝对路径 from parentx.parenty.C import something - Niel Godfrey Pablo Ponciano
我会尝试一下,谢谢! - chadlei
显示剩余2条评论

2

TL;DR

您可以通过为项目创建setup.py文件,然后运行pip install -e .来解决此问题,而不是修改sys.path

动机

这个答案可能与这个问题无关,但我把它放在这里是因为OP对这个解决方案表现出了兴趣,而且我认为这通常是比操作sys.path更可取的解决方案。

详情

我倾向于为我的项目创建一个实际的setup.py,就好像我要将它们发布到PyPI一样,然后运行pip install -e .将它们安装到我的(可能是虚拟的)Python环境中。

这是一个类似于我以前使用过的极简setup.py

from setuptools import setup

setup(
    name="project",
    version="0.0.1",
    python_requires=">=3.6",
    author="Some cool dude",
    long_description="Some cool project",
    install_requires=["dep1", "dep2"],
)

在另一个项目中,我在我的setup.py文件中读取了我的requirements.txt文件:
from setuptools import setup

with open("requirements.txt") as f:
    requirements = f.read().splitlines()

setup(
    name="project",
    version="0.0.1",
    python_requires=">=3.6",
    author="Some cool dude",
    long_description="Some cool project",
    install_requires=requirements,
)

无论使用哪种解决方案,setup.py文件都是我的project目录的同级目录(我总是使用小写字母命名我的项目),它们通常位于我的Git repo的根目录下,或者在src子目录下。
进入setyp.pyproject所在的目录,运行。
pip install -e .

现在你可以从任何地方导入project或其子模块,Python会找到它们。

建议阅读

有关setup.py文件的更多详细信息,请访问setup.py示例?


你能帮忙解决一个类似的问题吗?我在父目录中有一个文件(File A),它从与 A 相同的文件夹中导入 File B。File B 从它所在的文件夹中导入一个文件(File C)。如果我直接运行 File B,它可以正常运行。但是如果我运行 File A,File B 就会出现 ModuleNotFoundError:No module named 'File C' 错误。这种情况怎么办?@joanis - chadlei
这可能值得作为一个独立的问题来询问,并附上这个问题的指针以提供背景。但是,让我先试着理解一下... A、B和C在Project的父目录中吗?所以,它们不在你的Python路径目录中,对吗?它们在你运行代码的目录中吗? - joanis
A和包含B和C的文件夹Z在父目录中。我正在运行位于父目录中的A。它们不在我的Python路径中。 - chadlei
通常情况下,我总是使用完全限定的导入语句来指定导入的位置。因此,如果 Z.B 导入了 Z.C,我会在 Z.B 中使用 import Z.C,而不是依赖于文件的本地性。 - joanis
没问题!很高兴能帮忙。 - joanis
显示剩余2条评论

1
我在Windows操作系统中遇到了同样的问题。除了可能的级别注入作为父级之外,sys.path.append的问题是它创建了一个Windows路径,但由于某种原因Python没有搜索该路径,尽管它已经添加到sys.path中。相反,尝试使用:
ROOT_PATH = pathlib.Path(__file__).parents[1]
 sys.path.append(os.path.join(ROOT_PATH, ''))

它将以适当的方式将文件的父级目录添加到 sys.path。


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