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