Как создать пакет пространства имен в Python?

в Python пакет пространства имен позволяет распространять код Python среди нескольких проектов. Это полезно, когда вы хотите выпустить соответствующие библиотеки в виде отдельных загрузок. Например, с каталогами Package-1 и Package-2 на PYTHONPATH,

Package-1/namespace/__init__.py
Package-1/namespace/module1/__init__.py
Package-2/namespace/__init__.py
Package-2/namespace/module2/__init__.py

конечный пользователь может import namespace.module1 и import namespace.module2.

как лучше всего определить пакет пространства имен, чтобы несколько продуктов Python могли определять модули в этом пространстве имен?

5 ответов


TL; DR:

на Python 3.3 вам не нужно ничего делать, просто не кладите __init__.py в директории пакета и он будет просто работать. На pre-3.3, выберите pkgutil.extend_path() - решение pkg_resources.declare_namespace() один, потому что он защищен от будущего и уже совместим с неявными пакетами пространства имен.


Python 3.3 представляет неявные пакеты пространств имен, см. PEP 420.

это означает, что теперь есть три типа объектов это может быть создано import foo:

  • модуль, представленный
  • обычный пакет, представленный каталогом foo содержащий
  • пакет пространства имен, представленный одним или несколькими каталогами foo без __init__.py файлы

пакеты тоже являются модулями, но здесь я имею в виду" непакетный модуль", когда говорю"модуль".

сначала он сканирует sys.path для модуля или обычная упаковка. Если это удается, он прекращает поиск и создает и инициализирует модуль или пакет. Если он не нашел модуля или обычного пакета, но нашел хотя бы один каталог, он создает и инициализирует пакет пространства имен.

модули и обычные пакеты имеют __file__ установить на были созданы. Пакеты Regular и namespace имеют __path__установите в каталог или каталоги, из которых они были созданы.

когда вы import foo.bar выше поиск происходит сначала для foo, то если пакет был найден, поиск bar осуществляется с foo.__path__как путь поиска вместо sys.path. Если foo.bar найдено foo и foo.bar создаются и инициализируются.

Итак, как смешиваются обычные пакеты и пакеты пространства имен? Обычно они этого не делают, но старые pkgutil явный метод пакета пространства имен был расширен, чтобы включить неявные пакеты пространства имен.

если у вас уже есть обычный пакет это имеет __init__.py такой:

from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

... поведение legacy заключается в добавлении любого другого обычный пакеты по искомому пути к своему __path__. Но в Python 3.3 он также добавляет пакеты пространств имен.

так что вы можете иметь следующую структуру каталогов:

├── path1
│   └── package
│       ├── __init__.py
│       └── foo.py
├── path2
│   └── package
│       └── bar.py
└── path3
    └── package
        ├── __init__.py
        └── baz.py

... и пока эти двое ... --2--> есть extend_path строки (и path1, path2 и path3 в своем sys.path) import package.foo, import package.bar и import package.baz все работа.

pkg_resources.declare_namespace(__name__) не был обновлен для включения неявных пакетов пространства имен.


есть стандартный модуль, называемый pkgutil, С помощью которого вы можно "добавить" модули в заданное пространство имен.

со структурой каталогов, которую вы предоставили:

Package-1/namespace/__init__.py
Package-1/namespace/module1/__init__.py
Package-2/namespace/__init__.py
Package-2/namespace/module2/__init__.py

вы должны поместить эти две строки в оба Package-1/namespace/__init__.py и Package-2/namespace/__init__.py (*):

from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)

(* поскольку-если вы не указываете зависимость между ними - вы не знаете, какой из них будет распознан первым-см. PEP 420 для получения дополнительной информации)

как документация говорит:

это добавит к пакету __path__ все подкаталоги каталогов на sys.path назван в честь пакета.

С этого момента вы сможете распространять эти два пакета независимо друг от друга.


этот раздел должен быть довольно понятным.

короче говоря, поместите код пространства имен в __init__.py, update setup.py чтобы объявить пространство имен,и вы можете идти.


Это старый вопрос, но кто-то недавно прокомментировал мой блог, что моя публикация о пакетах пространств имен по-прежнему актуальна, поэтому я подумал, что я буду ссылаться на нее здесь, поскольку она дает практический пример того, как это сделать:

http://cdent.tumblr.com/post/216241761/python-namespace-packages-for-tiddlyweb

это ссылки на эту статью для основных кишок того, что происходит on:

http://www.siafoo.net/article/77#multiple-distributions-one-virtual-package

на __import__("pkg_resources").declare_namespace(__name__) трюк в значительной степени управляет управлением плагинами в TiddlyWeb и до сих пор, кажется, работает.


у вас есть концепции пространства имен Python назад к фронту, в python невозможно поместить пакеты в модули. Пакеты содержат модули, а не наоборот.

пакет Python-это просто папка, содержащая . Модуль-это любой другой файл в пакете (или непосредственно на PYTHONPATH), имеет