Как правильно использовать относительный или абсолютный импорт в модулях Python?

Использование относительного импорта в Python имеет один недостаток, вы больше не сможете запускать модули как автономные, потому что вы получите исключение: ValueError: Attempted relative import in non-package

# /test.py: just a sample file importing foo module
import foo
...

# /foo/foo.py:
from . import bar
...
if __name__ == "__main__":
   pass

# /foo/bar.py: a submodule of foo, used by foo.py
from . import foo
...
if __name__ == "__main__":
   pass

Как мне изменить код примера, чтобы иметь возможность выполнить все: test.py, foo.py и bar.py

Я ищу решение, которое работает с python 2.6+ (включая 3.x).

Ответ 1

Во-первых, я предполагаю, что вы понимаете, что вы написали, что приведет к циклической проблеме импорта, потому что foo import bar и viceversa; попробуйте добавить

from foo import bar

для test.py, и вы увидите, что он не работает. Этот пример должен быть изменен для работы.

Итак, то, что вы просите, действительно относится к абсолютному импорту, когда относительный импорт не удается; на самом деле, если вы выполняете foo.py или bar.py в качестве основного модуля, другие модули будут лежать на корневом уровне, и если они разделяют имя с другим модулем в системе, который будет выбран, зависит от порядок в sys.path. Поскольку текущий каталог обычно является первым, локальные модули будут выбраны, если они доступны, т.е. Если у вас есть файл "os.py" в текущем рабочем каталоге, он будет выбран вместо встроенного.

Возможное предложение:

foo.py

try:
    from . import bar
except ValueError:
    import bar

if __name__ == "__main__":
    pass

bar.py:

if __name__ == "__main__":
    pass

Кстати, вызов скриптов из правильной позиции обычно лучше.

python -m foo.bar

Вероятно, это лучший способ. Этот запускает модуль как script.

Ответ 2

Вы можете просто начать "запускать модули как автономные" по-другому:

Вместо:

python foo/bar.py

Использование:

python -mfoo.bar

Конечно, файл foo/__init__.py должен присутствовать.

Также обратите внимание, что у вас есть круговая зависимость между foo.py и bar.py - это не сработает. Я думаю, это просто ошибка в вашем примере.

Обновление: кажется, что он также отлично работает, чтобы использовать его в качестве первой строки foo/bar.py:

#!/usr/bin/python -mfoo.bar

Затем вы можете выполнить script непосредственно в системах POSIX.

Ответ 3

Относительный импорт канавок: в любом случае вы должны думать о пространстве имен пакетов как глобальном.

Трюк, чтобы сделать это приемлемым, редактирует sys.path соответствующим образом. Вот пища для размышлений:

# one directory up
_root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.insert(0, _root_dir)for now

Ответ 4

Вам нужно __init__.py в каждой папке.

Относительный импорт работает только тогда, когда вы делаете:

python test.py

test.py import foo.py и foo.py могут относить импортные данные из папки test.py и выше.

Вы не можете:

cd foo
python foo.py
python bar.py

Он никогда не будет работать.

Вы можете попробовать решение sys.path.append или sys.path.insert, но вы собираетесь испортить пути, и у вас будут проблемы с f = open (имя файла).

Ответ 5

Почему бы просто не поместить "main" в другой .py файл?

Ответ 6

До сих пор единственным решением, которое я нашел, было вовсе не использование относительного импорта.

Из-за текущего ограничения, мне интересно, когда кто-то должен использовать относительный импорт в python.

Во всех конфигурациях, в которых я использовал sys.path, содержался текущий каталог в качестве первого аргумента, поэтому просто используйте import foo вместо from . import foo, потому что он сделает то же самое.