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

StackOverflow https://stackoverflow.com/questions/1675734

  •  16-09-2019
  •  | 
  •  

Вопрос

В Python пакет namespace позволяет вам распространять код 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 могли определять модули в этом пространстве имен?

Это было полезно?

Решение

ТЛ;ДР:

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


В Python 3.3 представлены неявные пакеты пространства имен, см. ПЭП 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__) не был обновлен для включения пакетов неявного пространства имен.

Другие советы

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

С помощью предоставленной вами структуры каталогов:

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__)

(* поскольку - если вы не укажете зависимость между ними - вы не знаете, какой из них будет распознан первым - см. БОДРОСТЬ ДУХА 420 для получения дополнительной информации)

В качестве Документация говорит:

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

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

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

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

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

https://web.archive.org/web/20150425043954/http://cdent.tumblr.com/post/216241761/python-namespace-packages-for-tiddlyweb

Это ссылка на эту статью для получения основной информации о том, что происходит:

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

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

У вас есть концепции пространства имен 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top