Может ли кто-нибудь объяснить относительный импорт питона?

Я не могу на всю жизнь получить относительный импорт python для работы. Я создал простой пример того, где он не работает:

Структура каталогов:

/__init__.py
/start.py
/parent.py
/sub/__init__.py
/sub/relative.py

/start.py содержит только: import sub.relative

/sub/relative.py содержит только from .. import parent

Все остальные файлы пустые.

При выполнении в командной строке следующего:

$ cd /
$ python start.py

Я получаю:

Traceback (most recent call last):
  File "start.py", line 1, in <module>
    import sub.relative
  File "/home/cvondrick/sandbox/sub/relative.py", line 1, in <module>
    from .. import parent
ValueError: Attempted relative import beyond toplevel package

Я использую Python 2.6. Почему это так? Как сделать этот пример песочницы?

Ответ 1

Вы импортируете из пакета "sub". start.py не входит в пакет, даже если присутствует __init__.py.

Вам нужно будет запустить свою программу из одного каталога через parent.py:

./start.py

./pkg/__init__.py
./pkg/parent.py
./pkg/sub/__init__.py
./pkg/sub/relative.py

С start.py:

import pkg.sub.relative

Теперь pkg - это пакет верхнего уровня, и ваш относительный импорт должен работать.


Если вы хотите придерживаться своего текущего макета, вы можете просто использовать import parent. Поскольку вы используете start.py для запуска вашего интерпретатора, каталог, в котором находится start.py, находится в вашем пути python. parent.py живет там как отдельный модуль.

Вы также можете безопасно удалить верхний уровень __init__.py, если вы ничего не импортируете в script далее в дереве каталогов.

Ответ 2

Если вы собираетесь напрямую позвонить relative.py и, если вы действительно хотите импортировать из модуля верхнего уровня, вы должны явно добавить его в список sys.path.
Вот как это должно работать:

# Add this line to the beginning of relative.py file
import sys
sys.path.append('..')

# Now you can do imports from one directory top cause it is in the sys.path
import parent

# And even like this:
from parent import Parent

Если вы считаете, что это может привести к какой-то несогласованности, вы можете использовать это вместо:

sys.path.append(sys.path[0] + "/..")

sys.path[0] относится к пути, из которого была выполнена точка входа.