Как правильно использовать относительный или абсолютный импорт в модулях 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).

6 ответов

Решение

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

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

Это, наверное, лучший путь. Это запускает модуль как скрипт.

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

Вместо:

python foo/bar.py

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

python -mfoo.bar

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

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

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

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

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

Отказ от относительного импорта: вы все равно должны рассматривать пространство имен вашего пакета как глобальное.

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

# один каталог вверх
_root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.insert(0, _root_dir) сейчас

Тебе нужно __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(filename).

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

Пока единственное решение, которое я нашел, это не использовать относительный импорт вообще.

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

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

Другие вопросы по тегам