В 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 могли определять модули в этом пространстве имен?
python
namespaces
package
Joeforker
источник
источник
Ответы:
TL; DR:
В Python 3.3 вам не нужно ничего делать, просто не помещайте ничего
__init__.py
в каталоги пакетов вашего пространства имен, и это будет работать. В версиях до 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 он также добавляет пакеты пространства имен.Таким образом, у вас может быть следующая структура каталогов:
... и пока у двух
__init__.py
естьextend_path
строки (иpath1
,path2
иpath3
в вашемsys.path
)import package.foo
,import package.bar
иimport package.baz
все будет работать.pkg_resources.declare_namespace(__name__)
не был обновлен для включения неявных пакетов пространства имен.источник
namespace_packages
опцию? А в__import__('pkg_resources').declare_namespace(__name__)
чем дело?namespace_packages=['package']
вsetup.py
?namespace_packages=['package']
, setup.py добавитnamespace_packages.txt
в EGG-INFO. Все еще не знаю ударов…pkg_resources.declare_namespace
Over вpkgutil.extend_path
том, что он будет продолжать отслеживатьsys.path
. Таким образом, если новый элемент добавляетсяsys.path
после первой загрузки пакета в пространстве имен, тогда пакеты в пространстве имен в этом новом элементе пути по-прежнему могут быть загружены. (Преимущество использования__import__('pkg_resources')
over вimport pkg_resources
том, что вас неpkg_resources
выставляют наmy_namespace_pkg.pkg_resources
sys.path
. Приsys.path
изменении он проверяет, влияет ли это на__path__
какое-либо пространство имен, и если да, то обновляет эти__path__
свойства.Существует стандартный модуль 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 для получения дополнительной информации)
Как говорится в документации :
С этого момента вы сможете распространять эти два пакета независимо.
источник
__import__
в данном случае это считается плохим стилем, поскольку его можно легко заменить простым оператором импорта. Более того, pkg_resources - нестандартная библиотека. Он поставляется с инструментами настройки, так что это не проблема. Быстрый поиск в Google показывает, что pkgutil был представлен в версии 2.5, а pkg_resources предшествует ему. Тем не менее, pkgutil - это официально признанное решение. Фактически включение pkg_resources было отклонено в PEP 365.Package-1/namespace/__init__.py
и приPackage-2/namespace/__init__.py
условии, что мы не знаем, какой каталог пакета указан первым?Этот раздел не требует пояснений.
Короче говоря, вставьте код пространства имен
__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__)
Хитрость заключается в значительной степени приводит в действие управление плагинами в 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
источник
zope.x
несколько связанных пакетов, которые выпускаются как отдельные загрузки.