Подтвердить что ты не робот

Способ создания пакетов пространства имен в Python

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

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

__import__('pkg_resources').declare_namespace(__name__)

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

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

Пример

Я хотел бы предоставить подпакет 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
4b9b3361

Ответ 1

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

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

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

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

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

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

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

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