简体   繁体   中英

How can I fix these relative import error

I have a folder structure like this, each time I attempt to use relative import, error raises

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

All these codes are tested in 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 of the codes below cause any error:

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

I assume that when you say " tested in grok.py " that you are running it like these:

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

From the Python documentation on Packages and Intra-Package References , there is a note there that says:

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 .

When you run grok.py , it gets treated as the main module, and imports will only work if you used absolute imports (assuming you made no changes to sys.path , we'll get to that later). You can test that by putting print(__name__) at the start of grok.py, which will print out " __main__ ".

Your relative imports are actually going to work if you had a separate python file (ex. main.py ) under the graphics package that calls your grok module:

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

In main.py , let's just import the grok module:

from A import grok

In grok.py , let's test the relative imports:

from . import spam
spam.spam_func()

from .spam import spam_func
spam_func()

from B import bar
bar.bar_func()

In spam.py :

def spam_func():
    print("spammy")

In bar.py :

def bar_func():
    print("barry")

When you run main.py :

graphics$ python3 main.py
spammy
spammy
barry

You won't get any of the previous errors. The relative imports work. Notice that to import from B , I used from B instead of from ..B . This is because the import paths are from the point of view of main.py . You can test this by adding this at the top of main.py :

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

If you did from ..B that means /path/to/graphics/../ which of course does not have the B module (hence, you'll get the " attempted relative import beyond top-level package " error)


Now let's say you don't want to use a separate main.py and you want to run grok.py directly. What you can do is manually add the path to the graphics package to sys.path . Then you can do from A and from B in grok.py .

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

from A import spam
spam.spam_func()

from B import bar
bar.bar_func() 

If you want to go about "hacking" the sys.path , I suggest reading more on sys.path and checking other related posts that discuss ways of adding paths to sys.path .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM