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