6.4. Пакеты

Пакеты являются способом структурирования пространства имен Python с помощью "точечных имен модулей". Например, имя модуля A.B обозначает подмодуль с именем B в пакете с именем A. Такое использование модулей позволяет разработчикам различных модулей не беспокоится об именах глобальных переменных друг друга, использование точечных имен модулей позволяет авторам мультимодульных пакетов, таких как NumPy или Python Imaging Library, не беспокоиться об именах модулей друг друга.

Предположим, вы хотите разработать коллекцию модулей ("пакет") для единообразной обработки звуковых файлов и звуковых данных. Существует множество различных форматов звуковых файлов (обычно распознаваемых по их расширению, например: .wav, .aiff, .au), поэтому вам может потребоваться создавать и поддерживать растущую коллекцию модулей для конверсии между различными форматами файлов. Есть также множество различных операций, которые можно выполнять над звуковыми данными (такие как смешивание, добавление эхо, применение функции эквалайзера, создание искусственного стерео-эффекта), поэтому в дополнение вы будете писать нескончаемый поток модулей для выполнения этих операций. Здесь возможная структура для вашего пакета (выраженная в терминах иерархической файловой системы):

sound/                          Пакет верхнего уровня
      __init__.py               Инициализирует звуковой пакет
      formats/                  Подпакет для конверсии файловых форматов
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Подпакет для звуковых эффектов
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Подпакет для фильтров
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

При импорте пакета Python просматривает каталоги в соответствии с sys.path, обнаруживая подкаталоги пакета.

Фалы __init__.py требуются для того, чтобы Python обрабатывал каталоги как содержащие пакеты; это делается для предотвращения, что каталоги с часто употребляемыми именами, такими как string, не будут непреднамеренно скрывать действительные модули, которые обнаружатся позже по пути поиска модулей. В самом простом случае __init__.py может просто быть пустым файлом, но он также может выполнять инициализирующий код для пакета или устанавливать переменную __all__, описанную позже.

Пользователи пакета могут импортировать индивидуальные модули из пакета, например:

import sound.effects.echo

Загружается подмодуль sound.effects.echo. На него надо ссылаться по его полному имени:

sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

Альтернативный способ импорта подмодуля:

from sound.effects import echo

Также загружается подмодуль echo, но он доступен без его пакетного префикса, поэтому он может быть использован так:

echo.echofilter(input, output, delay=0.7, atten=4)

Еще одним вариантом является непосредственный импорт нужной функции или переменной:

from sound.effects.echo import echofilter

Опять же такой импорт загружает подмодуль echo, но это делает его функцию echofilter() доступной напрямую:

echofilter(input, output, delay=0.7, atten=4)

Заметьте, что когда используется from package import item, item может быть либо подмодулем (или подпакетом) пакета, либо каким-нибудь другим именем, определенным в пакете, как функция, класс или переменная. Оператор import сначала проверяет, есть ли определенный элемент в пакете; если нет, он предполагает, что это модуль и пытается загрузить его. Если он не находится, возбуждается исключение ImportError.

Наоборот, когда используется синтаксис как import item.subitem.subsubitem, каждый элемент исключается, и последний должен быть пакетом; последний элемент может быть модулем или пакетом, но не может быть классом или функцией или переменной, определенными в предыдущем элементе.

6.4.1. Импортирование * из пакета

Теперь что случится, когда пользователь напишет sound.effects import *? Хотелось бы надеяться, что в идеале это как-нибудь выходит на файловую систему, находит, какие подмодули есть в пакете, и импортирует их все. Это может занять долгое время и импортирование подмодулей может иметь нежелательный побочный эффект, который должен происходить только, когда подмодули явно импортированы.

Для разработчика пакета есть только одно решение - обеспечить явное индексирование пакета. Оператор import использует следующее соглашение: если код __init__.py пакета определяет список под названием __all__, он берется как список имен модуля, которые должны быть импортированы, когда встречается from package import *. Разработчик пакета должен обновлять список, когда выпускается новая версия пакета. Разработчики пакета могут также решить не поддерживать это, если они не видят использования для импортирования * из их пакета. Например, файл sound/effects/__init__.py может содержать следующий код:

__all__ = ["echo", "surround", "reverse"]

Это бы означало, что from sound.effects import * будет импортировать три названных подмодуля пакета sound.

Если __all__ не определена, выражение from sound.effects import * не импортирует все подмодули из пакета sound.effects в текущее пространство имен; это только обеспечивает то, что пакет sound.effects был импортирован (возможно выполняется какой-нибудь код инициализации из __init__.py) и затем импортирует любые имена, определенные в пакете. Это включает какие-либо имена определенные (и подмодули, загруженные явно) __init__.py. Это также включает какие-либо подмодули пакета, которые были явно загружены предыдущими операторами import. Рассмотрим этот код:

import sound.effects.echo
import sound.effects.surround
from sound.effects import *

В этом примере модули echo и surround импортируются в текущее пространство имен, потому что они определены в пакете sound.effects, когда выполняется выражение from...import (это также работает, когда определена __all__.)

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

Помните, нет ничего плохого в использовании from Package import specific_submodule! На самом деле это рекомендованная нотация, если не требуется из импортируемого модуля использовать подмодули с одинаковыми именами из различных пакетов.

6.4.2. Intra-package ссылки (справки, рекомендации)

Когда пакеты структурированы в подпакеты (как в пакете sound в примере), вы можете использовать абсолютные импорты для обращения к подмодулям сестринских пакетов. Например, если модулю sound.filters.vocoder надо использовать модуль echo в пакете sound.effects, он может использовать from sound.effects import echo.

Вы также можете писать относительные импорты, с from module import name формой оператора import. Такие импорты используют лидирующие точки для указания текущего родительского пакета, участвующего в относительном импорте. Например, из модуля surround вы можете использовать:

from . import echo
from .. import formats
from ..filters import equalizer

Заметьте, такой относительный импорт основывается на имени текущего модуля. Поскольку имя главного модуля всегда "__main__", модули, предназначенные для использования в качестве главного модуля приложений Python, должны всегда использовать абсолютные импорты.

6.4.3. Пакеты во множестве директорий

Пакеты поддерживают один достаточно специфический атрибут, __path__. Он инициализируется как список, содержащий имя каталога, хранящего __init__.py пакета, перед кодом в котором файл выполняется. Эта переменная может быть изменена; оказывая таким образом влияние на будущий поиск модулей и подпакетов, включенных в пакет.

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