Как правильно использовать относительный или абсолютный импорт в модулях Python? - PullRequest
26 голосов
/ 01 сентября 2010

Использование относительного импорта в 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 ]

22 голосов
/ 01 сентября 2010

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

Вместо:

python foo/bar.py

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

python -mfoo.bar

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

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

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

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

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

16 голосов
/ 06 сентября 2010

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

from foo import bar

to 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

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

1 голос
/ 12 февраля 2011

Вам нужно __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 (имя файла).

1 голос
/ 06 сентября 2010

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

Хитрость в том, чтобы сделать это приемлемым, - правильно отредактировать 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
0 голосов
/ 06 сентября 2010

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

0 голосов
/ 01 сентября 2010

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

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

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

...