Как мне создать пакет пространства имен в 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 ответов

Решение

Существует стандартный модуль 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 назван в честь пакета.

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

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.py файл
  • Обычный пакет, представленный каталогом foo содержащий __init__.py файл
  • Пакет пространства имен, представленный одним или несколькими каталогами foo без всяких __init__.py файлы

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

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

Модули и обычные пакеты имеют __file__ установить на .py файл, из которого они были созданы. Обычные и пространственные пакеты имеют __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__)

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

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

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

... и до тех пор, пока два __init__.py иметь extend_path линии (и path1, path2 а также path3 в вашем sys.path) import package.foo, import package.bar а также import package.baz все будет работать.

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

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

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

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

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

Это ссылки на эту статью, чтобы понять, что происходит:

http://www.siafoo.net/article/77

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

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

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

Итак, в вашем примере, предполагая, что Package-1 и Package-2 являются папками в файловой системе, которые вы указали в пути Python, вы можете получить следующее:

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

Теперь у вас есть одна посылка namespace с двумя модулями module1 а также module2, и если у вас нет веской причины, вы, вероятно, должны поместить модули в папку и иметь только этот путь по пути python, как показано ниже:

Package-1/
  namespace/
  __init__.py
  module1.py
  module2.py
Другие вопросы по тегам