Импортируйте модуль как из одного пакета, так и извне пакета в 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 from. import mod1 затем python3 interface.py работает; но mod2.py говорит, что SystemError: Parent module '' not loaded, cannot perform relative import :( :(

Единственное "решение", которое я нашел, - это зайти в одну папку и python -m pkg.mod2 и тогда оно python -m pkg.mod2. Но нужно ли добавлять префикс пакета pkg к каждому импорту в другие модули в этом пакете? Более того, чтобы запустить какие-либо скрипты внутри пакета, нужно ли помнить, чтобы перейти на одну папку вверх и использовать переключатель -m? Это единственный путь?

Я не совсем понимаю. Этот сценарий был довольно прост с Python 2, но выглядит неуклюжим в Python 3.

ОБНОВЛЕНИЕ: я загружаю эти файлы с (называемым выше "решением") рабочим исходным кодом здесь: https://gitlab.com/Akronix/test_python3_packages. Обратите внимание, что мне все еще не нравится это, и он выглядит намного хуже, чем решение python2.


Связанные ТАК вопросы, которые я уже прочитал:

Ссылки по теме:

Ответ 1

TL;DR:

  • Запустите ваш код с помощью python -m pkg.mod2.
  • Импортируйте свой код с помощью from. import mod1 from. import mod1.

Единственное "решение", которое я нашел, - это зайти в одну папку и python -m pkg.mod2 и тогда оно python -m pkg.mod2.

Использование переключателя -m действительно является "единственным" решением - раньше оно уже было единственным. Старое поведение просто всегда срабатывало из чистой удачи; это может быть сломано даже без изменения вашего кода.

Переход "на одну папку вверх" просто добавляет ваш пакет в путь поиска. Установка вашего пакета или изменение пути поиска также работает. Смотрите ниже для деталей.

Но нужно ли добавлять префикс пакета pkg к каждому импорту в другие модули в этом пакете?

У вас должна быть ссылка на ваш пакет - в противном случае не ясно, какой модуль вы хотите. Ссылка на пакет может быть абсолютной или относительной.

Относительный импорт, как правило, то, что вы хотите. Это позволяет сэкономить на написании pkg, что упрощает рефакторинг и перемещение модулей.

# inside mod1.py
# import mod2 - this is wrong! It can pull in an arbitrary mod2 module
# these are correct, they uniquely identify the 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. Несмотря на то, что во многих случаях он был простым, его было трудно или просто невозможно исправить ни в каких других случаях.

Новое поведение является более неуклюжим в прямом случае, но надежным и надежным в любом случае.