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