Шаг 185.
Python: сборник рецептов. Модули и пакеты. Импортирование подмодулей пакета с использованием относительных имен

    На этом шаге мы рассмотрим особенности такого импортирования.

Задача

    У вас есть упорядоченный в пакет код, и вы хотите импортировать подмодуль из одного из подмодулей другого пакета без прямого прописывания (hardcoding) имени пакета в инструкции импортирования.

Решение

    Чтобы импортировать модули пакета из других модулей в том же пакете, используйте импортирование относительно пакета. Предположим, например, что у вас есть пакет mypackage, который организован следующим образом:

mypackage/
    __init__.py
    A/
        __init__.py
        spam.py
        grok.py
    В/
        __init__.py
        bar.py

    Если модуль mypackage.A.spam пожелает импортировать модуль grok, размещенный в том же каталоге, он должен будет обзавестись такой инструкцией:

# mypackage/A/spam.py 

from . import grok

    Если тот же модуль захочет импортировать модуль B.bar, размещенный в другом каталоге, вы можете использовать такую инструкцию:

# mypackage/A/spam.py 

from ..B import bar

    Обе показанные инструкции импортирования работают относительно местоположения файла spam.py и не включают имя пакета высшего уровня.

Обсуждение

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

# mypackage/A/spam.py

from mypackage.A import grok # ОК 
from . import grok           # OK
import grok                  # Ошибка (не найден)

    Недостаток использования абсолютного имени, такого как mypackage.A, заключается в том, что оно напрямую прописывает имя пакета высшего уровня в исходный код. Это, в свою очередь, делает ваш код более хрупким и затрудняет работу по реорганизации. Например, если вы когда-либо захотите поменять имя пакета, вам придется пройтись по всем файлам и поправить исходный код. Похожим образом напрямую прописанные имена затрудняют перемещение кода. Например, если кто-то захочет установить две разные версии пакета, различающиеся только именами, то при использовании относительного импортирования все будет работать хорошо, но абсолютные имена не позволят этого сделать.

    Синтаксис инструкции import . и .. может показаться забавным, но предлагаем вам думать о нем как об определении имени каталога. Символы .. означают "ищи в текущем каталоге", а ..B - "ищи в каталоге ../В". Этот синтаксис работает только с формой from. Например:

from . import grok   # OK
import . grok        # Ошибка

    Хотя это выглядит так, будто вы можете перемещаться по файловой системе через относительное импортирование, но на самом деле это не позволит вам выйти из каталога, в котором определен пакет. Это означает, что комбинирование имен с точками, которое заставляет производить импортирование из-за пределов пакета, вызовет ошибку.

    Также стоит заметить, что относительное импортирование работает только для модулей, которые размещены внутри подходящего пакета. В частности, оно не работает внутри простых модулей, размещенных на верхнем уровне скриптов. Оно также не работает, если части пакета исполняются напрямую, как скрипты. Например:

% python3 mypackage/A/spam.py
# Относительное импортирование не работает

    С другой стороны, если вы выполните предыдущий скрипт, передав в Python опцию -m, относительное импортирование будет работать правильно. Например:

% python3 -m mypackage.A.spam
# Относительное импортирование работает
За дополнительными сведениями об относительном импортировании пакетов обратитесь к PEP 328: https://peps.python.org/pep-0328/.

    На следующем шаге мы рассмотрим разделение модуля на несколько файлов.




Предыдущий шаг Содержание Следующий шаг