如何修复这些相对导入错误

How can I fix these relative import error

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

├── 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

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

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

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

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

Packages and Intra-Package References 的 Python 文档中,有一条注释说:

Note that relative imports are based on the name of the current module. Since the name of the main module is always "__main__", modules intended for use as the main module of a Python application must always use absolute imports.

当您 运行 grok.py 时,它被视为主模块,导入仅在您使用绝对导入时才有效(假设您未对 sys.path 进行任何更改,我们稍后再讲)。您可以通过将 print(__name__) 放在 grok.py 的开头来测试它,这将打印出“__main__”。

如果您在调用您的图形包下有一个单独的 python 文件(例如 main.py),那么您的相对导入 实际上 会起作用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 模块(因此,你会得到“attempted relative import超出 top-level 包 " 错误)


现在假设您不想使用单独的 main.py,而是想直接 运行 grok.py。您可以做的是手动将 graphics 包的路径添加到 sys.path。然后你可以在 grok.py.

中做 from Afrom B
import sys
sys.path.append("/full/path/to/graphics/")

from A import spam
spam.spam_func()

from B import bar
bar.bar_func() 

如果您想了解 "hacking" 和 sys.path,我建议您多阅读 sys.path and checking other related posts that discuss ways of adding paths to sys.path