Способ создания пакетов пространства имен в Python - PullRequest
13 голосов
/ 05 декабря 2011

Из Пакеты пространств имен в дистрибутиве , я знаю, что могу использовать пакеты пространства имен для разделения большого пакета Python на несколько меньших. Это действительно потрясающе. В документе также упоминается:

Кстати, обратите внимание, что исходное дерево вашего проекта должно включать Пакеты пространства имен ’__init__.py файлы (и __init__.py любого родительские пакеты), в обычной компоновке пакетов Python. Эти __init__ .py файлы должны содержать строку:

__import__('pkg_resources').declare_namespace(__name__)

Этот код гарантирует, что оборудование пакета пространства имен работает и что текущий пакет зарегистрирован как пакет пространства имен.

Мне интересно, есть ли какие-либо преимущества в сохранении той же иерархии каталогов, что и иерархия пакетов? Или это только техническое требование к функции пакетов пространств имен в дистрибутиве / setuptools?

Ex

Я хотел бы предоставить подпакет foo.bar , чтобы мне пришлось построить следующую иерархию папок и подготовить __init__.py, чтобы setup.py работал с пакетом пространства имен:

~foo.bar/
~foo.bar/setup.py
~foo.bar/foo/__init__.py    <=    one-lined file dedicated to namespace packages
~foo.bar/foo/bar/__init__.py
~foo.bar/foo/bar/foobar.py

Я не знаком с пакетами пространства имен, но мне кажется, что 1) foo / bar и 2) (почти) однорядный __init__.py являются рутинными задачами. Они дают некоторые подсказки о том, что «это пакет пространства имен», но я думаю, что у нас уже есть эта информация в setup.py ?

редактирование:

Как показано в следующем блоке, могу ли я иметь пакет пространства имен без этого вложенного каталога и однострочного __init__.py в моем рабочем каталоге? То есть мы можем попросить setup.py автоматически сгенерировать их, просто поставив одну строку namespace_packages = ['foo']?

~foo.bar/
~foo.bar/setup.py
~foo.bar/src/__init__.py    <=    for bar package
~foo.bar/src/foobar.py

1 Ответ

41 голосов
/ 05 декабря 2011

Пакет пространства имен в основном имеет особый эффект, когда приходит время импортировать подпакет. Вот что происходит при импорте foo.bar

  • импортер просматривает sys.path в поисках чего-то похожего на foo.
  • когда он что-то найдет, он заглянет внутрь обнаруженного foo на bar.
  • , если bar не найдено:
    1. , если foo - нормальный пакет, ImportError повышается, указывая, что foo.bar не существует.
    2. , если foo является пакетом пространства имен , импортер возвращается к просмотру sys.path для следующего совпадения foo. ImportError повышается, только если все пути исчерпаны.

Так вот что он делает , но не объясняет, почему вы можете этого хотеть. Предположим, вы разработали большую полезную библиотеку (foo), но в рамках этого вы также разработали небольшую, но очень полезную утилиту (foo.bar), которую другие программисты Python находят полезной, даже если они не используют ее. для большей библиотеки.

Вы можете распространять их вместе как один большой пакет (как вы его разработали), даже если большинство людей, использующих его, импортируют только субмодуль. Вашим пользователям было бы очень неудобно, потому что они должны были бы загрузить все это (все 200 МБ!), Даже если они действительно заинтересованы только в служебном классе из 10 строк. Если у вас есть открытая лицензия, вы, вероятно, обнаружите, что несколько человек в конечном итоге ее разветвляют, и теперь существует полдюжины расходящихся версий вашего служебного модуля.

Вы можете переписать всю свою библиотеку так, чтобы утилита находилась вне пространства имен foo (просто bar вместо foo.bar). Вы сможете распространять утилиту отдельно, и некоторые из ваших пользователей будут счастливы, но это большая работа, особенно учитывая, что на самом деле пользователей используют целую библиотеку, и поэтому они Придется переписывать свои программы, чтобы использовать новые.

Так что вам действительно нужен способ установки foo.bar самостоятельно, но, к счастью, он сосуществует с foo, когда это тоже необходимо.

Пакет пространства имен позволяет именно это, две полностью независимые установки пакета foo могут сосуществовать. setuptools распознает, что эти два пакета предназначены для совместного использования, и вежливо смещает папки / файлы таким образом, что оба находятся на пути и отображаются как foo, один из которых содержит foo.bar, а другой содержит остальное foo.

У вас будет два разных setup.py скрипта, по одному для каждого. foo/__init__.py в обоих пакетах должно указывать, что они являются пакетами пространства имен, поэтому импортер знает, что продолжить, независимо от того, какой пакет был обнаружен первым.

...