| ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
Встроенная поддержка пакетов в Python 1.5 Начиная с Python версии 1.5a4, в интерпретатор встроена поддержка пакетов. Она реализует немного упрощенную и модифицированную версию семантики импорта пакетов, введенную модулем "ni". "Импортирование пакета" - это метод для структурирования пространств имен модулей Python используя "точечные имена модулей". Например, модуль с именем A.B определяет подмодуль B в пакете A. В то время как использование модулей позволяет авторам других модулей не беспокоиться о глобальных именах остальных, применение точечных имен модулей предохраняет многомодульные пакеты вроде NumPy или PIL от конфликта с именами других модулей. Еще в версии Python 1.3 была добавлена поддержка импорта пакетов с помощью стандартного библиотечного модуля "ni". (Предполагают, что это аббревиатура для "New Import" - "новое импортирование", но на самом деле, это название связано с Knights Who Say Ni в фильме Monty Python and the Holy Grail, которые после возвращения рыцарей короля Артура с кустарником, сменили имя на Knights Who Say Neeeow ... Wum ... Ping - но это уже другая история.) Модуль ni является полностью пользовательским кодом за исключением некоторых изменений в разборщике Python (также введенных в 1.3) для обеспечения импорта в виде "import A.B.C" и "from A.B.C import X". Когда ni еще не доступен, использование такого синтаксиса приведет к ошибке времени выполнения "No such module". Если же ni доступен (после выполнения "import ni" до импортирования других модулей), то он будет перехватывать импортирование подмодулей корректных пакетов. Новая поддержка пакетов напоминает ni, но более гибкая и немного подправленная. Пример Допустим, мы хотим разработать пакет единообразной обработки звуковых файлов и данных. Существует много различных форматов звуковых файлов (обычно различаемых по расширению: .wav, .aiff, .au), поэтому нам может понадобиться создание и поддержка растущего набора модулей для конвертации различных форматов файлов. Еще может быть много других операций, которые придется производить над звуковыми данными (напр., смешивание, добавление эха, применение функций эквалайзера, создание искусственных стерео эффектов), так что, потребуется написание бесконечного потока модулей. Ниже приводится возможная структура для наших пакетов (выраженная в терминах иерархических файловых систем): Sound/ Верхне-уровневый пакет __init__.py Инициализация пакета Utils/ Под-пакет для внутреннего использования __init__.py iobuffer.py errors.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 dolby.py ... Пользователи пакета могут загружать из него отдельные пакеты, например:
При использовании Обратно, при использовании синтаксиса Импортирование * из пакета; атрибут Что же теперь происходит когда пользователь пишет
Единственный выход для автора пакета -- предоставить явный
индекс пакета. Оператор import использует следующее соглашение:
если __init__.py пакета определяет список под именем __all__,
то он считается списком имен модулей, которые должны быть импортированы
при встрече команды __all__ = ["echo", "surround", "reverse"]И это означало бы, что from Sound.Effects import * будет
импортировать три перечисленных подмодуля из пакета Sound.
Если __all__ не определен, оператор В этом примере, модули echo и surround импортируются в текущее пространство имен, поскольку они определены в пакете Sound.Effects где выполнился оператор from...import. (Это работает даже тогда, когда __all__ определен.)import Sound.Effects.echo import Sound.Effects.surround from Sound.Effects import * Имейте в виду, что на практику импортирования * из модуля или пакета обычно смотрят с неодобрением, поскольку это часто приводит к коду, читаемому с трудом. Однако, это полезно для экономии печатания в интерактивных сеансах, когда необходимые модули сконструированы так, что экспортируют только имена, следующие определенным шаблонам. Помните, что нет ничего плохого в использовании Внутри-пакетные ссылки Подмодули часто нуждаются в ссылках между собой. Например,
модуль окружения, возможно, захочет использовать модуль эха. Фактически,
подобные ссылки настолько общи, что оператор импорта сперва сканирует
включающий пакет перед тем, как обратиться к пути поиска стандартных
модулей. Так, модуль окружения может просто использовать
Когда пакеты структурированы в под-пакеты (как пакет Sound в примере),
не существует сокращения для ссылки на подмодули родственных пакетов,
нужно использовать полное имя под-пакета. Например, если модуль
Sound.Filters.vocoder нуждается в использовании модуля эха в пакете
Sound.Effects, то он может написать
(Можно было бы создать нотацию для ссылки в родительский пакет, подобно использованию ".." при обращении к родительскому каталогу в файловой системе Unix и Windows. Фактически, ni поддерживает это, используя __ для пакета, содержащего текущий модуль, __.__ для родительского пакета, и т.д. Эта особенность была отброшена из-за ее неуклюжести; поскольку большинство пакетов будут иметь относительно мелкую подструктуру, это не такая уж большая потеря.) Детали Пакеты тоже модули! Внимание: нижеследующее может запутать тех, кто знаком с нотацией пакетов в Java, которая похожа на нотацию Python, но отличается от нее. Когда бы ни загружался подмодуль пакета, Python убеждается, что
сам пакет был сперва загружен, в случае необходимости подгружает файл
__init__.py. Аналогично и для пакетов. Так, при выполнении оператора
После загрузки, разница между пакетом и модулем минимальна. Фактически, оба представлены в виде модульных объектов, и каждый хранится в таблице загруженных модулей -- sys.modules. Ключи к ним в sys.modules задаются в виде полных, разделенных точками имен модулей (которые не всегда совпадают с теми, что записаны в операторе импорта). Это является также содержимым переменной __name__ (которая дает полное имя модуля или пакета). Переменная __path__ Единственное различие между пакетами и модулями проявляется в наличии или отсутствии переменной __path__. Она имеется только у пакетов. Эта переменная инициализируется списком из одного элемента, содержащего имя каталога пакета (подкаталог каталога в sys.path). При изменении __path__ меняется список каталогов в которых производится поиск подмодулей пакета. Например, пакет Sound.Effects мог бы содержать подмодули, специфичные для платформы. При этом можно было использовать следующую структуру каталогов: Sound/ __init__.py Effects/ # Основные версии модулей эффектов __init__.py echo.py surround.py reverse.py ... plat-ix86/ # Модули эффектов, специфичные для Intel x86 echo.py surround.py plat-PPC/ # Модули эффектов, специфичные для PPC echo.py Файл Effects/__init__.py мог бы манипулировать своей переменной __path__ так, чтобы подходящие платформо-специфичные поддиректории загружались перед главной директорией Effects, с тем, чтобы реализация определенных эффектов, специфичная для платформы, переопределяла общую (вероятно более медленную) реализацию. Пример: platform = ... # Выясни текущую платформу dirname = __path__[0] # Главный каталог пакета __path__.insert(0, os.path.join(dirname, "plat-" + platform)) Если нежелательно, чтобы платформо-специфичные модули прятали основные модули с такими же именами, то следует использовать __path__.append(...) вместо __path__.insert(0, ...). Примечание: поддиректории plat-* не являются под-пакетами для Effects, файл Sound/Effects/plat-PPC/echo.py соответствует модулю Sound.Effects.echo.py. Ложные записи в При использовании пакетов, Вы можете неожиданно обнаружить случайные записи в sys.modules, напр., sys.modules['Sound.Effects.string'] может оказаться равным None. Это "косвенный" элемент, образовавшийся из-за того, что некоторые подмодули в пакете Sound.Effects импортировали верхне-уровневый модуль string. Целью этого является важная оптимизация: поскольку оператор импорта не может угадать, локальный ли нужен модуль или глобальный, и еще потому, что правилами определяется приоритет локальных модулей (в том же пакете) над глобальными, с такими же именами, то оператор импорта должен просмотреть путь поиска пакетов прежде, чем станет искать (возможно уже импортированные) глобальные модули. А так как просмотр пути поиска пакетов относительно дорогая операция, и импортирование уже импортированных модулей считается менее накладным (порядка нескольких просмотров словаря), то с оптимизацией все в порядке. Ложные записи позволяют избежать просмотра пути пакета, когда такой же глобальный модуль был со второго раза импортирован подмодулем этого пакета. Ложные записи создаются только для модулей, найденных на верхнем уровне; если модуль совсем не был найден, то импорт терпит неудачу и оптимизация обычно не нужна. Кроме того, при интерактивном использовании, пользователь мог создать модуль как локальный пакетный подмодуль, и снова повторить импортирование; если бы были сделаны ложные записи, то он не был бы найден. Если пользователь изменяет структуру пакета, путем создания локального подмодуля с тем же именем, что у глобального модуля уже использованного в пакете, то результат обычно известен как "беспорядок", и соответствующее решение состоит в том, чтобы выйти из интерпретатора и запустить его снова. А что если у меня модуль и пакет имеют одинаковые имена? У Вас может быть каталог (в sys.path), в котором находится как модуль spam.py так и подкаталог spam, содержащий __init__.py (без __init__.py каталог не распознается как пакет). В этом случае подкаталог имеет приоритет и импортирование spam будет игнорировать файл spam.py, загружая вместо него пакет spam. Если Вы хотите, чтобы модуль spam.py имел приоритет, то он должен находиться в каталоге, встречающемся раньше в sys.path. (Окончание: порядок поиска определяется списком суффиксов, возвращаемых функцией imp.get_suffixes(). Обычно порядок поиска для суффиксов следующий: ".so", "module.so", ".py", ".pyc". Директории явно не присутствуют в этом списке, но предшествуют всем элементам этого списка.) Предложение по инсталляции пакетов Для того, чтобы Python программа использовала пакет, он должен отыскиваться оператором импорта. Другими словами, пакет должен быть подкаталогом каталога в sys.path. Традиционно, самый простой способ быть уверенным, что пакет находится в sys.path, было либо инсталлирование его в стандартную библиотеку, либо расширение пользовательского sys.path, путем задания его переменной окружения $PYTHONPATH. На практике, оба решения быстро вызывают хаос. Выделенные каталоги В Python 1.5 было установлено соглашение, предотвращающее хаос, путем предоставления системному администратору больше контроля. Прежде всего, два дополнительных каталога добавлены в конец пути поиска по умолчанию (четыре, если префикс установки и exec_prefix отличаются). Они зависят от префикса установки (по умолчанию /usr/local):
Каталог site-packages можно использовать для пакетов, которые вероятно зависят от версий Python (напр., пакет содержащий разделяемые библиотеки или использующий новые особенности). Каталог site-python используется для обратной совместимости с Python 1.4 и для чисто Python пакетов или модулей, не зависящих от используемой версии Python. Рекомендуемое использование этих каталогов состоит в размещении
каждого пакета в собственную поддиректорию внутри каталога site-packages
или site-python. Поддиректория должна иметь имя пакета, которое будет
доступно как идентификатор Python. После этого, любая Python программа
может импортировать модули из пакета путем задания их полных имен.
Например, пакет Sound, использованный в примере, могла бы быть
инсталлирована в каталог $prefix/lib/python1.5/site-packages/Sound,
что сделает возможным операторы импорта подобные
Дополнение уровня косвенности В некоторых местах желательно инсталлировать пакеты не в упомянутые каталоги, но так, чтобы они были доступны всем Python программам, запускаемым всеми пользователями. Этого можно достичь двумя различными способами:
Примечание для платформ Mac и Windows На Mac и Windows, соглашения немного другие. Каталогом для инсталляции пакетов на этих платформах является корневой каталог (или подкаталог) Python, который специфичен инсталлированной версии Python. И это также (единственный) каталог, в котором ищутся файлы конфигурации (*.pth). Поддиректории каталога стандартной библиотеки Поскольку любой подкаталог каталога sys.path подразумевается возможным для использования в качестве пакета, можно легко войти в заблуждение относительно тех, которые и не предполагаются как таковые. Например, предположим, что имеется подкаталог, под именем tkinter, содержащий модуль Tkinter.py. Как следует писать -- import Tkinter или import tkinter.Tkinter? Если подкаталог tkinter находится в пути, то оба способа будут работать, но это создает ненужную неразбериху. Установлено простое соглашение по именованию, которое должно ликвидировать эту неразбериху: не-пакетные каталоги должны иметь дефис в своем имени. В частности, все платформо-специфичные подкаталоги (sunos5, win, mac, и т.д.) были переименованы с префиксом "plat-". Подкаталоги, специфичные для необязательных компонентов Python, которые еще не были конвертированы в пакеты, переименованы с префиксом "lib-". Подкаталог dos_8x3 был переименован в dos-8x3. В следующей таблице приведены все переименованные директории:
Примечание, что подкаталог тестирования еще не переименован. Теперь
это пакет. Для его вызова, используйте оператор вроде Другие вещи XXX У меня еще не было времени обсудить следующие пункты: Изменения в Следующие особенности ni были сдублированны непосредственно. Игнорируйте эту секцию, если Вы в настоящее время не используете модуль ni и хотите перейти к использованию встроенной поддержки модулей. Выброшенный По умолчанию, когда подмодуль пакета A.B.C импортирует модуль X, ni будет искать A.B.C.X, A.B.X, A.X и X, в том же порядке. Раньше это определялось в пакете, с помощью переменной __domain__, которой можно было присваивать список имен пакетов для поиска. Данная особенность ликвидирована во встроенной поддержке пакетов. Вместо этого, всегда сперва ищется A.B.C.X, а затем X. (Это изменение для достижения "двух областей" удачно используется еще кое-где в Python, для решений, связанных с пространствами имен.) Выброшенный При использовании ni, пакеты могли явно задавать "относительные" имена модулей, используя специальное имя "__" (два подчеркивания). К примеру, модули в пакете A.B.C могли ссылаться на модули, определенные в пакете A.B.K, через имя в виде __.__.K.модуль . Эта особенность была выброшена из-за ее ограниченного использования и неважной удобочитаемости. Несовместимая семантика для При использовании ni, файл __init__.py в пакете (в случае его
существования) был бы импортирован как стандартный подмодуль пакета.
Встроенная же поддержка пакетов вместо этого загружает файл __init__.py
в пространство имен пакета. Это означает, что если __init__.py в пакете
A определяет имя x, то на него можно будет сослаться как A.x, без
всякого дополнительного усилия. При использовании ni, __init__.py
должен был бы содержать присваивание в виде Кроме того, новая поддержка пакетов требует, чтобы
Пакеты, желающие быть обратно совместимыми с ni, могут проверять существует ли специальная переменная __ , напр.: # Определить функцию, видимую на уровне пакета def f(...): ... try: __ except NameError: # новая встроенная поддержка пакетов pass else: # обратная совместимость для ni __.f = f |
|
| ||||||||||||||||
|