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

174

Я не могу на всю жизнь заставить относительный импорт 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. Почему это так? Как мне заставить этот пример с песочницей работать?

деревенщина
источник

Ответы:

140

Вы импортируете из пакета "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, если вы ничего не импортируете в скрипт дальше по дереву каталогов.

Ebo
источник
2
Вы путаете термины «модуль» и «пакет». 'start.py' представляет модуль 'start', 'mod' и 'mod.sub' являются пакетами, 'mod' является пакетом верхнего уровня.
Фердинанд Бейер
34
Спасибо, но это, честно говоря, кажется очень глупым. Для такого красивого языка, я не могу поверить, что дизайнеры создали бы такое ограничение. Разве нет другого пути?
Карл
2
Это совсем не глупо. Относительный импорт - это средство ссылки на одноуровневые модули в пакете. Если вы хотите импортировать модуль верхнего уровня, используйте абсолютный импорт.
Фердинанд Бейер
58
Не глупо? Так что, в bash, вы не могли бы обратиться к относительному верхнему каталогу с помощью ".."
е-удовлетворяться
2
Мне кажется, что идея питона состоит в том, чтобы использовать «абсолютный» импорт из каталога, в который вы запустили свой родительский скрипт. Таким образом, вы можете использовать абсолютный путь «import parent» для импорта родительского модуля из одного уровня. И относительный импортирует какое-то наследство или что-то еще ..
Одиссей
35

Если вы собираетесь позвонить 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] относится к пути, с которого была запущена точка входа.

AmirHossein
источник
3

Проверяем это в python3:

python -V
Python 3.6.5

Example1:

.
├── parent.py
├── start.py
└── sub
    └── relative.py

- start.py
import sub.relative

- parent.py
print('Hello from parent.py')

- sub/relative.py
from .. import parent

Если мы запустим это так (просто чтобы убедиться, что PYTHONPATH пуст):

PYTHONPATH='' python3 start.py

Вывод:

Traceback (most recent call last):
  File "start.py", line 1, in <module>
    import sub.relative
  File "/python-import-examples/so-example-v1/sub/relative.py", line 1, in <module>
    from .. import parent
ValueError: attempted relative import beyond top-level package

Если мы изменим импорт в sub/relative.py

- sub/relative.py
import parent

Если мы запустим это так:

PYTHONPATH='' python3 start.py

Вывод:

Hello from parent.py

Example2:

.
├── parent.py
└── sub
    ├── relative.py
    └── start.py

- parent.py
print('Hello from parent.py')

- sub/relative.py
print('Hello from relative.py')

- sub/start.py
import relative
from .. import parent

Запустите это как:

PYTHONPATH='' python3 sub/start.py

Вывод:

Hello from relative.py
Traceback (most recent call last):
  File "sub/start.py", line 2, in <module>
    from .. import parent
ValueError: attempted relative import beyond top-level package

Если мы изменим импорт в sub/start.py:

- sub/start.py
import relative
import parent

Запустите это как:

PYTHONPATH='' python3 sub/start.py

Вывод:

Hello from relative.py
Traceback (most recent call last):
  File "sub/start.py", line 3, in <module>
    import parent
ModuleNotFoundError: No module named 'parent'

Запустите это как:

PYTHONPATH='.' python3 sub/start.py

Вывод:

Hello from relative.py
Hello from parent.py

Также лучше использовать импорт из корневой папки, то есть:

- sub/start.py
import sub.relative
import parent

Запустите это как:

PYTHONPATH='.' python3 sub/start.py

Вывод:

Hello from relative.py
Hello from parent.py
mrgloom
источник