Иногда возникает необходимость разделить несколько пакетов, расположенных в одном пространстве имен по разным физическим путям.
Например, если вы хотите иметь возможность передавать разные макеты плагинов, иметь возможность позже добавлять их, не контролируя их местоположение, и в то же время получать к ним доступ через одно и то же пространство имен.
Эта шпаргалка, больше подходящая для новичков, посвящена пространствам имен Python. Давайте посмотрим, как это можно сделать в разных версиях Python, поскольку хотя Python2 скоро перестанет поддерживаться, многие из нас сейчас находятся между двух огней, и это один из важных нюансов при переходе.
Рассмотрим этот пример:
Мы хотим получить структуру пакета:
Содержимое файла mod1namespace1 package1 module1 package2 module2
print('package 1')
var1 = 1
Содержимое файла Module2
print('package 2')
var2 = 2
Пакеты распространяются в следующей структуре папок:
path1
namespace1
package1
module1
path2
namespace1
package2
module2
Предположим, что пути path1 и path2 каким-то образом уже добавлены в sys.path. Нам нужен доступ к модулю1 и модулю2:
from namespace1.package1 import module1
from namespace1.package2 import module2
Что произойдет в Питон 3.7 при выполнении этого кода? Все работает чудесно:
package 1
package 2
В PEP-420 в Python 3.3 появилась поддержка неявных пространств имен.
Кроме того, при импорте пакета из версии py33 вам не нужно создавать файлы __init__.py. А при импорте пространства имен это просто _запрещено_. Если файл __init__.py существует в одном или обоих каталогах и пространстве имен 1, при импорте второго пакета произойдет ошибка.
ModuleNotFoundError: No module named 'namespace1.package2'
Таким образом, наличие инициализатора четко определяет пакет, и пакеты нельзя объединять, это единая сущность.
Если вы начинаете новый проект и пакеты, которые не зависят от старых разработок и будут установлены с помощью pip, вам следует следовать этому методу.
Однако иногда мы наследуем старый код, который тоже необходимо поддерживать, хотя бы какое-то время, или переводить в новую версию.
Давайте перейдем к Питон 2.7 .
С этой версией уже интереснее, нужно сначала добавить __init__.py в каждую директорию для создания пакетов, иначе интерпретатор просто не распознает пакет в этом наборе файлов.
Затем напишите явное объявление пространства имен в файлах __init__, относящихся к пространству имен 1, иначе будет импортирован только первый пакет. from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
Что происходит? Когда интерпретатор достигает первого импорта, он ищет в sys.path пакет с таким именем, который находится в пути1/пространстве имен1, и интерпретатор выполняет путь1/пространство имен1/__init__.py. Дальнейшего поиска нет. Однако сама функция расширения_path просматривает весь sys.path, находит все пакеты с именем namespace1 и инициализатором и добавляет их в переменную __path__ пакета namespace1, которая используется для поиска дочерних пакетов в этом пространстве имен.
Официальные руководства рекомендуют, чтобы инициалы были одинаковыми при каждом размещении пространства имен1. На самом деле все они могут быть пустыми, кроме первого, который находится при поиске в sys.path, в котором должен быть вызов pkgutil.extend_path, поскольку остальные не выполняются.
Однако, конечно, лучше действительно иметь вызов в каждом инишнике, чтобы не завязывать свою логику «случайно» и не гадать, какой инишник выполнился первым, ведь порядок поиска может измениться.
По той же причине вы не должны размещать какую-либо другую логику в файлах переменной области __init__. Это будет работать в будущих версиях, и этот код можно использовать для написания совместимый код , но нужно учитывать, что выбранный метод должен соблюдаться в каждом распространяемом пакете.
Если в версии 3 вы поместите в некоторые пакеты исходный файл, вызвав pkgutil.extend_path, а некоторые оставите без исходного файла, то это не сработает.
Кроме того, этот вариант подойдет и для случая, когда вы планируете установку с помощью python setup.py install.
Еще один метод, который сейчас считается несколько устаревшим, но все еще можно найти во многих местах: #namespace1/__init__.py
__import__('pkg_resources').
declare_namespace(__name__)
Модуль pkg_resources поставляется с пакетом setuptools. Смысл здесь тот же, что и в pkgutil — каждый файл __init__ должен содержать одно и то же объявление пространства имен и никакого другого кода для каждого размещения пространства имен1. В этом случае пространство имен namespace_packages=['namespace1'] должно быть зарегистрировано в setup.py. Более подробное описание создания пакетов выходит за рамки данной статьи.
Кроме того, часто можно встретить такой код try:
Теги: #python #программирование #python3 #python2 #python2
-
Распределение Посетителей По Ресурсам Google
19 Oct, 24 -
В Баню С It
19 Oct, 24 -
Я Богат
19 Oct, 24 -
Рай И Ад
19 Oct, 24