Импортировать модуль как внутри одного пакета, так и снаружи пакета в Python 3
Хорошо, сценарий очень прост. У меня есть такая структура файлов:
.
├── interface.py
├── pkg
│ ├── __init__.py
│ ├── mod1.py
│ ├── mod2.py
Вот мои условия:
- мод2 нужно импортировать мод1.
- и interface.py, и mod2 должны запускаться независимо как основной скрипт. Если хотите, воспринимайте интерфейс как актуальную программу, а mod2 как внутренний тестер пакета.
Итак, в Python 2 я бы просто сделал import mod1
внутри mod2.py и оба python2 mod2.py
а также python2 interface.py
будет работать как ожидалось.
Тем не менее, и это часть, которую я менее понимаю, используя Python 3.5.2, если я делаю import mod1
; тогда я могу сделать python3 mod2.py
, но python3 interface.py
броски: ImportError: No module named 'mod1'
:(
Так что, по-видимому, Python 3 предлагает использовать import pkg.mod1
чтобы избежать столкновений со встроенными модулями. Хорошо, если я использую это, я могу сделать python3 interface.py
; но тогда я не могу python3 mod2.py
так как: ImportError: No module named 'pkg'
Аналогично, если я использую относительный импорт:from . import mod1
затем python3 interface.py
работает; но mod2.py говорит SystemError: Parent module '' not loaded, cannot perform relative import
:(:(
Единственное "решение", которое я нашел, - это зайти в одну папку и сделать python -m pkg.mod2
и тогда это работает. Но нужно ли добавлять префикс пакета? pkg
для каждого импорта в другие модули в этом пакете? Более того, чтобы запустить какие-либо скрипты внутри пакета, нужно ли помнить, чтобы перейти на одну папку вверх и использовать ключ -m? Это единственный путь?
Я не совсем понимаю. Этот сценарий был довольно прост с Python 2, но выглядит неуклюжим в Python 3.
ОБНОВЛЕНИЕ: Я загружаю эти файлы с (называемым выше "решением") рабочим исходным кодом здесь: https://gitlab.com/Akronix/test_python3_packages. Обратите внимание, что мне все еще не нравится это, и выглядит намного хуже, чем решение python 2.
Связанные ТАК вопросы, которые я уже прочитал:
- Python - импортировать пакет в модуль, который находится внутри того же пакета
- Как сделать относительный импорт в Python?
- Абсолютный модуль импорта в той же упаковке
Ссылки по теме:
2 ответа
TLDR:
- Запустите свой код с
python -m pkg.mod2
, - Импортируйте свой код с
from . import mod1
,
Единственное "решение", которое я нашел, - это зайти в одну папку и сделать
python -m pkg.mod2
и тогда это работает.
С использованием -m
switch действительно является "единственным" решением - раньше оно было единственным. Старое поведение просто всегда срабатывало из чистой удачи; это может быть сломано даже без изменения вашего кода.
Переход на "одну папку вверх" просто добавляет ваш пакет в путь поиска. Установка вашего пакета или изменение пути поиска также работает. Смотрите ниже для деталей.
Но нужно ли добавлять префикс пакета pkg к каждому импорту в другие модули в этом пакете?
У вас должна быть ссылка на ваш пакет, иначе не ясно, какой модуль вы хотите. Ссылка на пакет может быть абсолютной или относительной.
Относительный импорт, как правило, то, что вы хотите. Это экономит письмо pkg
явно облегчая рефакторинг и перемещение модулей.
# mod1.py
# import mod2 - this is wrong! It can pull in an arbitrary mod2 module
import pkg.mod2
from pkg import mod2
from . import mod2
from .mod2 import foo # if pkg.mod2.foo exists
Обратите внимание, что вы всегда можете использовать <import> as <name>
привязать ваш импорт к другому имени. Например, import pkg.mod2 as mod2
позволяет работать только с именем модуля.
Более того, чтобы запустить какие-либо скрипты внутри пакета, нужно ли помнить, чтобы перейти на одну папку вверх и использовать ключ -m? Это единственный путь?
Если ваш пакет установлен правильно, вы можете использовать -m
переключаться из любого места. Например, вы всегда можете использовать python3 -m json.tool
,
echo '{"json":"obj"}' | python -m json.tool
Если ваш пакет не установлен (пока), вы можете установить PYTHONPATH
в его базовый каталог. Это включает ваш пакет в пути поиска и позволяет -m
переключиться, чтобы найти его правильно.
Если вы находитесь в каталоге исполняемого файла, вы можете выполнить export PYTHONPATH="$(pwd)/.."
быстро смонтировать пакет для импорта.
Я не совсем понимаю. Этот сценарий был довольно прост с Python 2, но выглядит неуклюжим в Python 3.
Этот сценарий был в основном нарушен в Python 2. Несмотря на то, что во многих случаях он был простым, его было трудно или просто невозможно исправить ни в каких других случаях.
Новое поведение более неуклюже в прямом случае, но надежно и надежно в любом случае.
У меня была аналогичная проблема. Я решил это, добавив
импорт системы sys.path.insert(0,".package_name")
в__init__.py
файл в папке пакета.