相对导入 - 模块未找到错误:没有名为x的模块

417

这是我第一次认真尝试Python 3,但似乎失败了。我有以下两个文件:

  1. test.py
  2. config.py

config.py中定义了几个函数和几个变量。我已将其简化为以下内容:

config.py

debug = True

test.py

import config
print (config.debug)

我也有一个__init__.py

但是,我遇到了以下错误:

ModuleNotFoundError: No module named 'config'

我知道在py3中习惯使用绝对导入:

from . import config

然而,这会导致以下错误:

ImportError: cannot import name 'config'

所以我现在不知道该怎么办... 非常感谢任何帮助。:)


2
我无法重现这个错误,你是如何执行这段代码的? - Copperfield
3
我使用Python自带的IDLE来执行它,也可以通过python test.py命令来执行,都能正常运行。虽然我没有PyCharm,但可能是PyCharm的某些配置问题导致了这个问题。 - Copperfield
2
非常奇怪。我正在使用WinPython - 只需从python.org下载vanilla Python 3.6,它就可以正常工作。从未想过检查解释器!谢谢! - blitzmann
2
我的猜测是PYTHONPATH出了点问题。请检查您的IDE设置和/或系统环境变量。 - Martin Tournoij
4
我有完全相同的问题。这不是PyCharm的问题!这是Python3的问题。在Python2中可以工作,但是在使用Python3时,你会看到这个错误!非常令人沮丧。 - user3159377
显示剩余5条评论
19个回答

328

简而言之:您无法从您执行的文件中进行相对导入,因为__main__模块不是包的一部分。

绝对导入 - 导入在sys.path中可用的内容

相对导入 - 导入相对于当前模块的内容,必须是包的一部分

如果您以完全相同的方式运行两种变体,则其中一种应该有效。以下示例应该可以帮助您理解正在发生的情况。让我们添加另一个名为main.py的文件,并将整体目录结构设为:

.
./main.py
./ryan/__init__.py
./ryan/config.py
./ryan/test.py

让我们更新test.py以查看正在进行的操作:

# config.py
debug = True
# test.py
print(__name__)

try:
    # Trying to find module in the parent package
    from . import config
    print(config.debug)
    del config
except ImportError:
    print('Relative import failed')

try:
    # Trying to find module on sys.path
    import config
    print(config.debug)
except ModuleNotFoundError:
    print('Absolute import failed')
# main.py
import ryan.test

先运行test.py

$ python ryan/test.py
__main__
Relative import failed
True

这里的“test”__main__模块,并不知道自己属于一个包。然而,import config应该可以工作,因为ryan文件夹将被添加到sys.path中。

让我们运行main.py

$ python main.py
ryan.test
True
Absolute import failed

这里的测试位于“ryan”包内部,并且可以执行相对导入操作。由于Python 3不允许隐式相对导入,因此import config会失败。

希望这有所帮助。

P.S .:如果您坚持使用Python 3,则不再需要__init__.py文件。


5
有没有什么方法可以让绝对导入始终起作用?比如,在/some/path/my_module/__init__.py 中调用 sys.path.append('/some/path/my_module') ? - James T.
5
@JamesT. 是的,在运行时修改 sys.path 是相当常见的操作 (https://github.com/search?q=sys.path.append&type=Code&utf8=%E2%9C%93)。您也可以设置 PYTHONPATH 环境变量。 - Igonato
13
有趣。如果您坚持使用Python 3,就不再需要使用__init__.py文件了。您能详细解释一下吗?我曾经认为包解析机制在2和3之间并没有太大变化。 - Kevin
4
如果你继续使用Python 3,就不再需要__init__.py文件了。相反,如果我们希望一个包能在2和3中都起作用,我们需要查看2009年那篇已经过时的文章"What is __init__.py for?"以及它最受欢迎的答案"It's a part of a package"。我们需要始终强调和频繁地区分"常规包[旧版本,3.3之前]"和"命名空间包[3.3+]"。 - smci
谢谢你的回答。我在想,"from ryan import test" 在 main.py 中是否等同于 "import ryan.test"(忽略名称空间前缀的差异,即在前者中我们可以通过test访问,而在后者中我们必须通过ryan.test访问)?这两个导入方式是否将导入test命名空间中定义的相同变量集,并且它们都是绝对导入吗? - torez233
显示剩余4条评论

150
你需要将你的项目路径添加到PYTHONPATH中,并确保使用绝对导入
对于UNIX(Linux,OSX等)
export PYTHONPATH="${PYTHONPATH}:/path/to/your/project/"

适用于Windows操作系统

set PYTHONPATH=%PYTHONPATH%;C:\path\to\your\project\

绝对导入

假设我们有以下项目结构:

└── myproject
    ├── mypackage
    │   ├── __init__.py
    │   ├── a.py
    └── anotherpackage
        ├── __init__.py
        ├── b.py
        ├── c.py
        └── mysubpackage
            ├── __init__.py
            └── d.py

只需确保每个导入都从项目的根目录开始引用。例如,

# in module a.py
import anotherpackage.mysubpackage.d

# in module b
import anotherpackage.c
import mypackage.a

如需更全面的解释,请参考文章如何修复ModuleNotFoundError和ImportError


7
非常感谢 @Giorgos!尤其是当你试图在 Docker 镜像中设置根目录时。 - Tony Fraser
这个方法对我在Ubuntu上有效:export PYTHONPATH="/home/me/projects/foo:$PYTHONPATH" - DanielBell99

116
我已经理解了。这非常令人沮丧,特别是对于那些从python2转移过来的人。
无论模块是相对路径还是绝对路径,都必须在模块后添加一个 .
我按照以下方式创建了目录设置。
/main.py
--/lib
  --/__init__.py
  --/mody.py
  --/modx.py

modx.py

def does_something():
    return "I gave you this string."

mody.py

from modx import does_something

def loaded():
    string = does_something()
    print(string)

main.py

from lib import mody

mody.loaded()

当我执行main方法时,会发生以下情况。
$ python main.py
Traceback (most recent call last):
  File "main.py", line 2, in <module>
    from lib import mody
  File "/mnt/c/Users/Austin/Dropbox/Source/Python/virtualenviron/mock/package/lib/mody.py", line 1, in <module>
    from modx import does_something
ImportError: No module named 'modx'

我运行了2to3,核心输出如下:

RefactoringTool: Refactored lib/mody.py
--- lib/mody.py (original)
+++ lib/mody.py (refactored)
@@ -1,4 +1,4 @@
-from modx import does_something
+from .modx import does_something

 def loaded():
     string = does_something()
RefactoringTool: Files that need to be modified:
RefactoringTool: lib/modx.py
RefactoringTool: lib/mody.py

我不得不修改mody.py的导入语句来修复它。
try:
    from modx import does_something
except ImportError:
    from .modx import does_something


def loaded():
    string = does_something()
    print(string)

然后我再次运行了main.py,得到了预期的输出。
$ python main.py
I gave you this string.

最后,只需要清理一下并使其在2和3之间可移植。
from __future__ import absolute_import
from .modx import does_something

3
值得注意的是,try/except的加载程序是真正起作用的关键因素(因为一些人需要使用try:scripts.modxexcept: modx),并且正是这一点为我解决了这个问题。 - Justapigeon
在导入时使用try/except很丑陋。我希望有更好的解决方案。 - ingyhere
5
无论模块是相对路径还是绝对路径,都需要在模块名称前添加一个点号"."。这个说法并不完全准确。如果你使用了点号,那么这将成为一种相对导入方式。 - Giorgos Myrianthous
1
非常感谢!这个点(.)就是我的实际问题 =) - Aidos
1
我正在使用一个名为generateDS的库,并在模块名称处添加了一个点,于2021年解决了这个问题!谢谢! - etoricky
在Python 3.7中,从文件导入时添加“.”(例如:from .file1 import my_var)仍然非常有效。谢谢。 - altela

63

设置PYTHONPATH也可以帮助解决此问题。

以下是在Windows上执行的方法:

set PYTHONPATH=.


14
将PYTHONPATH设置为主代码目录对我解决了这个问题! - Geek
9
可以在Linux上运行。导出PYTHONPATH=。 - rjdkolb
同样适用于MacOS。export PYTHONPATH=. - Thales

28

您可以简单地将以下文件添加到您的测试目录中,然后Python会在运行测试之前运行它。

__init__.py file

import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

1
这正是我一直在寻找的。感谢分享这个答案!!! - Mayur
1
像这样做会导致一个linter(pylint3)错误。错误类似于这个。 filename.py:12:0: C0413: 导入“import abc.def.ghi.file_util as file_util”应该放在模块的顶部(wrong-import-position) - Sharad
很好。这些代码用于 Python 3.6.6 的 init 文件中。 - VISQL

20
在根项目目录下设置PYTHONPATH环境变量。
考虑类UNIX操作系统:
export PYTHONPATH=.

这个像魔法一样好用!非常感谢。 - user7630232

16
如果您正在使用Python 3+,请尝试添加以下代码行。
import os, sys
dir_path = os.path.dirname(os.path.realpath(__file__))
parent_dir_path = os.path.abspath(os.path.join(dir_path, os.pardir))
sys.path.insert(0, parent_dir_path)

2
同样的答案还有另一个版本:sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir))。这里省略了一些内容,例如:os.path.abspathos.path.realpath - Harshad Vyawahare

15

尝试了你的例子

from . import config

遇到以下SystemError错误:
/usr/bin/python3.4 test.py
Traceback (most recent call last):
File "test.py", line 1, in
from . import config
SystemError: 未加载父模块“”,无法执行相对导入


这对我有用:

import config
print('debug=%s'%config.debug)

>>>debug=True

使用Python: 3.4.2 - PyCharm 2016.3.2进行测试


除此之外,PyCharm还提供了导入该名称的选项。
您需要点击config,然后会出现一个帮助图标输入图像描述


10

在调用模块之前,请先声明正确的 sys.path 列表:

import os, sys

#'/home/user/example/parent/child'
current_path = os.path.abspath('.')

#'/home/user/example/parent'
parent_path = os.path.dirname(current_path)

sys.path.append(parent_path)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'child.settings')

6

我正在一台Linux机器上工作。当我运行 python my_module/__main__.py 时,遇到相同的问题。

如果在运行脚本之前运行命令 export PYTHONPATH=. ,则该错误将被修复。

export PYTHONPATH=.
python my_module/__main__.py

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