Как я могу исправить эти относительные ошибки импорта - PullRequest
1 голос
/ 05 июня 2019

У меня есть такая структура папок, каждый раз, когда я пытаюсь использовать относительный импорт, возникает ошибка

├── 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 Ответ

2 голосов
/ 06 июня 2019

Я предполагаю, что когда вы говорите " испытания в grok.py ", вы запускаете его так:

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

Из документации Python по Пакетам и Внутрипакетным ссылкам есть примечание:

Обратите внимание, что относительный импорт основан на названии текущего модуль. Поскольку имя основного модуля всегда "__main__", модули, предназначенные для использования в качестве основного модуля приложения Python всегда должен использовать абсолютный импорт .

Когда вы запускаете 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 (следовательно, вы получите ошибку " попытка относительного импорта за пределы пакета верхнего уровня ") )


Теперь предположим, что вы не хотите использовать отдельный main.py и хотите запустить grok.py напрямую. Что вы можете сделать, это вручную добавить путь к пакету graphics в sys.path. Тогда вы можете сделать from A и from B в 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() 

Если вы хотите заняться «взломом» sys.path, я предлагаю прочитать больше на sys.path и проверить другие связанные посты, в которых обсуждаются способы добавления путей к sys.path.

...