如何解决这些相对导入错误

时间:2019-06-05 00:13:32

标签: python python-3.6 python-import

我有这样的文件夹结构,每次尝试使用相对导入时,都会引发错误

├── graphics
│   ├── __init__.py
│   ├── A
│   │   ├── __init__.py
│   │   ├── grok.py
│   │   └── spam.py
    └── B
        ├── __init__.py
        └── bar.py


spam.py/
    def func():
        pass
bar.py/
    def f():
        pass

所有这些代码都在grok.py中进行了测试:

from . import spam
# ImportError: cannot import name 'spam'

from .spam import func
# ModuleNotFoundError: No module named '__main__.spam'; '__main__'     
is not a package

from ..B import bar
# ValueError: attempted relative import beyond top-level package

以下代码均不会导致任何错误:

from graphics.A import spam
from graphics.A.spam import func
from graphics.B import bar
from graphics.B.bar import f

1 个答案:

答案 0 :(得分:2)

我假设当您说“ 在grok.py中测试过”时,您正在像这样运行它:

python3 graphics/A/grok.py
python3 A/grok.py
python3 grok.py

PackagesIntra-Package References上的Python文档中,有一条注释说:

  

请注意,相对导入基于当前名称   模块。由于主模块的名称始终为“ __main__”,   旨在用作Python应用程序主模块的模块   必须始终使用绝对导入

当您运行grok.py时,它将被视为主模块,并且只有在您使用绝对导入的情况下,导入才起作用(假设您未对sys.path进行任何更改,我们将在后面介绍)。您可以通过将print(__name__)放在grok.py的开头来进行测试,这将打印出“ __main__”。

如果在调用main.py模块的图形包下有单独的python文件(例如grok),则相对导入实际上将正常工作:

├── graphics
│   ├── __init__.py
|   ├── main.py     <<---- add this
│   ├── A
│   ├── B

main.py中,我们仅导入grok模块:

from A import grok

grok.py中,我们测试相对导入:

from . import spam
spam.spam_func()

from .spam import spam_func
spam_func()

from B import bar
bar.bar_func()

spam.py中:

def spam_func():
    print("spammy")

bar.py中:

def bar_func():
    print("barry")

运行main.py时:

graphics$ python3 main.py
spammy
spammy
barry

您不会收到任何先前的错误。相对进口工作。请注意,要从B导入,我使用了from B而不是from ..B。这是因为导入路径是从main.py的角度来看的。您可以通过在main.py顶部添加以下内容来进行测试:

import sys
print(sys.path)
# prints a list, ['/path/to/graphics/',...]

如果您进行了from ..B的操作,则意味着/path/to/graphics/../当然没有B模块(因此,您将获得“ 尝试的相对导入超出顶级”包”错误)


现在让我们说您不想使用单独的main.py,而您想直接运行grok.py。您可以做的是手动将graphics软件包的路径添加到sys.path。然后,您可以在from A中进行from Bgrok.py

import sys
sys.path.append("/full/path/to/graphics/")

from A import spam
spam.spam_func()

from B import bar
bar.bar_func() 

如果您想“入侵” sys.path,建议您阅读sys.path的更多内容,并查看其他讨论ways of adding paths to sys.path的相关文章。