Как правильно использовать относительный или абсолютный импорт в модулях 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).
Пока единственное решение, которое я нашел, это не использовать относительный импорт вообще.
Из-за текущих ограничений мне интересно, когда кто-то должен использовать относительный импорт в python.
На всех конфигурациях, которые я использовал sys.path
содержит текущий каталог в качестве первого аргумента, так что просто используйте import foo
вместо from . import foo
потому что он будет делать то же самое.