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

использование относительного импорта в Python имеет один недостаток, вы больше не сможете запускать модули как standalones, потому что вы получите исключение: 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.икс.)

6 ответов


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

from foo import bar

к test.py и ты увидишь, что это не сработает. Пример должен быть изменен, чтобы работать.

Итак, то, что вы просите, - это действительно вернуться к абсолютному импорту при сбое относительного импорта; на самом деле, если вы выполняете foo.py или bar.py как основной модуль, другие модули будут просто лежать на корневом уровне, и если они поделитесь своим именем с другим модулем в системе, который будет выбран, зависит от заказа в sys.путь. Поскольку текущий dir обычно является первым, локальные модули будут выбраны, если они доступны - то есть, если у вас есть 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

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


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

вместо:

python foo/bar.py

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

python -mfoo.bar

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

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

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

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

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


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

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

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

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

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

python test.py

test.py импорт foo.py и foo.py может ли относительный импорт чего-либо из папки test.py и выше.

вы не можете сделать:

cd foo
python foo.py
python bar.py

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

вы можете попробовать sys.путь.добавление или sys.путь.вставьте решение, но вы испортите пути, и у вас будут проблемы с F=open(filename).


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


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

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

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