Як створити пакет простору імен у Python?


141

У 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
Мені здається, що module1 та module2 - це фактично субпакети, а не модулі. Як я розумію, модуль - це в основному один файл. Можливо, subpkg1 та subpkg2 мали б більше сенсу як імена?
Алан

Відповіді:


79

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 він також додає пакети простору імен.

Таким чином, ви можете мати таку структуру каталогів:

├── 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__) не було оновлено, щоб включати неявні пакети простору імен.


2
А як з setuptools? Чи потрібно використовувати namespace_packagesваріант? А __import__('pkg_resources').declare_namespace(__name__)річ?
кавінг-чиу

3
Чи слід додати namespace_packages=['package']в setup.py?
Лоран ЛАПОРТЕ

1
@clacke: З namespace_packages=['package'], setup.py додасть namespace_packages.txtв EGG-INFO. Досі не знаю наслідків…
Лоран ЛАПОРТЕ

1
@ kawing-chiu Перевага pkg_resources.declare_namespaceнад pkgutil.extend_pathтим, що він буде продовжувати контролювати sys.path. Таким чином, якщо новий елемент додається sys.pathпісля того, як пакет в просторі імен спочатку завантажується, то пакунки в просторі імен у цьому новому елементі шляху все ще можуть бути завантажені. (Користь використання __import__('pkg_resources')над import pkg_resourcesтим, що ви не pkg_resourcesmy_namespace_pkg.pkg_resources
піддаєтесь

1
@clacke Це не працює так (але це має такий же ефект, як якщо б це було). Він підтримує глобальний перелік усіх просторів імен пакетів, створених за допомогою цієї функції, і дивиться sys.path. При sys.pathзмінах він перевіряє, чи впливає це __path__на будь-який простір імен, і якщо це відбувається, то він оновлює ці __path__властивості.
Артур Такка

81

Існує стандартний модуль, який називається 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ім'ям пакета.

Відтепер ви зможете самостійно поширювати ці два пакети.


17
Які плюси і мінуси використання , що по порівнянні з імпортом __ ( «pkg_resources»). Declare_namespace (__ ім'я )?
joeforker

14
По-перше, __import__вважається поганим стилем у цьому випадку, оскільки його можна легко замінити звичайним заявою про імпорт. Більш того, pkg_resources - це нестандартна бібліотека. Він поставляється з setuptools, так що це не проблема. Швидке гуглінг показує, що pkgutil був введений у 2,5, а pkg_resources перед цим. Тим не менш, pkgutil є офіційно визнаним рішенням. Фактично включення pkg_resources було відхилено в PEP 365.
Майк Гордецький

3
Цитата PEP 382 : Поточний імперативний підхід до пакетів простору імен призвів до декількох трохи несумісних механізмів надання пакетів простору імен. Наприклад, pkgutil підтримує * .pkg файли; setuptools не робить. Аналогічно, setuptools підтримує перевірку zip-файлів та підтримує додавання частин до змінної _namespace_packages, тоді як pkgutil - ні.
Дрейк Гуан

7
Чи не слід ці два рядки вводити в обидва файли: Package-1/namespace/__init__.py і за Package-2/namespace/__init__.py умови, що ми не знаємо, який саме реєстр пакунків перерахований першим?
Була

3
@ChristofferKarlsson так, це справа, все гаразд, якщо ви знаєте, що є першим, але справжнє питання полягає в тому, чи можете ви гарантувати, що він буде першим у будь-якій ситуації, тобто для інших користувачів?
Була

5

Цей розділ повинен бути досить зрозумілим.

Якщо коротко, введіть код простору імен __init__.py, оновіть, setup.pyщоб оголосити простір імен, і ви можете вільно зайти.


9
Ви завжди повинні цитувати відповідну частину посилання, на випадок, коли відповідне посилання загине.
Tinned_Tuna

2

Це давнє запитання, але хтось нещодавно прокоментував у своєму блозі, що моє повідомлення про пакети просторів імен залишається актуальним, тому подумав, що я посилаюся на нього тут, оскільки це дає практичний приклад того, як зробити його:

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 і до сих пір , здається, працює поза.


-9

У вас є поняття простору імен 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коли купу пов'язаних пакетів випускається як окремі завантаження.
joeforker

Гаразд, але який ефект ви намагаєтеся досягти. Якщо папки, що містять відповідні пакети, усі на PYTHONPATH, інтерпретатор Python знайде їх для вас без зайвих зусиль з вашого боку.
Tendayi Mawushe

5
Якщо ви додасте і Package-1, і Package-2 до PYTHONPATH, Python буде бачити лише Package-1 / простір імен.
Søren Løvborg
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.