TL; DR:
W Pythonie 3.3 nie musisz nic robić, po prostu nie umieszczaj żadnych __init__.py
w katalogach pakietów przestrzeni nazw i po prostu zadziała. W wersjach wcześniejszych niż 3.3 wybierz pkgutil.extend_path()
rozwiązanie zamiast tego pkg_resources.declare_namespace()
, ponieważ jest przyszłościowe i już jest zgodne z niejawnymi pakietami przestrzeni nazw.
Python 3.3 wprowadza niejawne pakiety przestrzeni nazw, zobacz PEP 420 .
Oznacza to, że istnieją teraz trzy typy obiektów, które można utworzyć za pomocą import foo
:
- Moduł reprezentowany przez
foo.py
plik
- Zwykły pakiet reprezentowany przez katalog
foo
zawierający __init__.py
plik
- Pakiet przestrzeni nazw, reprezentowany przez jeden lub więcej katalogów
foo
bez żadnych __init__.py
plików
Pakiety też są modułami, ale mam tu na myśli „moduł nie będący pakietem”, kiedy mówię „moduł”.
Najpierw skanuje w sys.path
poszukiwaniu modułu lub zwykłego pakietu. Jeśli się powiedzie, zatrzymuje wyszukiwanie i tworzy oraz inicjalizuje moduł lub pakiet. Jeśli nie znalazł modułu lub zwykłego pakietu, ale znalazł przynajmniej jeden katalog, tworzy i inicjalizuje pakiet przestrzeni nazw.
Moduły i zwykłe pakiety mają __file__
ustawiony .py
plik, z którego zostały utworzone. Pakiety zwykłe iz przestrzeni nazw __path__
ustawiły katalog lub katalogi, z których zostały utworzone.
Kiedy to zrobisz import foo.bar
, powyższe wyszukiwanie odbywa się najpierw dla foo
, a następnie, jeśli pakiet został znaleziony, wyszukiwanie bar
jest wykonywane foo.__path__
za pomocą ścieżki wyszukiwania zamiast sys.path
. Jeśli foo.bar
zostanie znaleziony, foo
i foo.bar
są tworzone i inicjalizowane.
Jak więc mieszają się zwykłe pakiety i pakiety przestrzeni nazw? Zwykle tak nie jest, ale stara pkgutil
jawna metoda pakietu przestrzeni nazw została rozszerzona o niejawne pakiety przestrzeni nazw.
Jeśli masz istniejący zwykły pakiet, który ma taki __init__.py
wygląd:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
... starsze zachowanie polega na dodaniu innych zwykłych pakietów na przeszukiwanej ścieżce do ich __path__
. Ale w Pythonie 3.3 dodaje również pakiety przestrzeni nazw.
Możesz więc mieć następującą strukturę katalogów:
├── path1
│ └── package
│ ├── __init__.py
│ └── foo.py
├── path2
│ └── package
│ └── bar.py
└── path3
└── package
├── __init__.py
└── baz.py
... i tak długo, jak obaj __init__.py
mają extend_path
linie (i path1
, path2
i path3
są w twoim sys.path
) import package.foo
, import package.bar
i import package.baz
wszystko będzie działać.
pkg_resources.declare_namespace(__name__)
nie został zaktualizowany w celu uwzględnienia niejawnych pakietów przestrzeni nazw.