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