Pull to refresh

О порядке поиска пакетов и модулей для импорта в Python

Reading time5 min
Views59K
Начать, видимо, следует с того, что речь пойдет об интерпретаторе CPython версии 2.7.x (примеры проверялись на версии 2.7.3).

На официальном сайте имеются описания инструкции import и модулей в Python:

Из них следует, что в Python имеются пакеты (package), модули (module) и имена, определенные в модулях (names). Также следует отметить, что в некоторых частях документации модули называются подмодулями (submodule), если они размещены внутри пакета.

В языке Python инструкция import позволяет импортировать пакеты, модули и имена в пространство имен, в котором инструкция import выполняется. При это существует две интересные особенности:
  1. Из синтаксиса инструкции import не всегда явно следует, что именно должно быть импортированно: пакет, модуль или имя
  2. Синтаксисом инструкции import невозможно явно указать, что путь к модулю является абсолютным путем (хотя явно указать, что путь является относительным можно, а также возможно изменение семантики инструкции, в части использования абсолютного пути по умолчанию, см. www.python.org/dev/peps/pep-0328 )

Из этих двух особенностей следуют такие неоднозначности для записи import abcd:
  1. Импортировать ПАКЕТ abcd, либо импортировать МОДУЛЬ abcd
  2. Импортировать пакет/модуль abcd из ТЕКУЩЕГО ПАКЕТА (из пакета того модуля, в котором исполняется import abcd), либо ИЗ ПАКЕТА в соответствии с перечнем каталогов, указанных в sys.path

Еще примеры неоднозначностей:
  • from abcd import defg: (импортировать модуль defg из пакета abcd, либо импортировать пакет defg из пакета abcd, либо импортировать имя defg из пакета abcd, либо импортировать имя defg из модуля abcd) X (из того же пакета, либо из пакета в соответствии с sys.path)
  • import abcd.defg: (импортировать пакет defg из пакета abcd, импортировать модуль defg из пакета abcd) X (из того же пакета, либо из пакета в соответствии с sys.path)

Для разрешения эти декларативных неоднозначностей должен существовать императивный алгоритм. Такой алгоритм в некотором виде описан в официальной документации Python.


Алгоритм поиска имен для import abcd


Поиск имени abcd для импорта происходит по следующему алгоритму:
Что ищем Где ищем* Комментарий
1 пакет abcd в пакете текущего модуля (модуля, в котором исполняется import abcd) только, если текущий модуль сам содержится в пакете**
2 модуль abcd в пакете текущего модуля (модуля, в котором исполняется import abcd) только, если текущий модуль сам содержится в пакете**
3 модуль abcd во встроенных (built-in) модулях ссылка на документацию указана в ***
4 пакет abcd в каталогах, указанных в sys.path ссылка на документацию указана в ****
5 модуль abcd в каталогах, указанных в sys.path ссылка на документацию указана в ****


Дальнейший поиск прекращается в случае успешного поиска пакета на одном из выше перечисленных шагов.

* Информация о приоритете поиска пакета над модулем установленая опытным путем, в документации это явно не указано.

** В этом случае переменная __package__ этого модуля равна названию пакета, иначе она равна None.
Ссылка на документцию:
docs.python.org/2/tutorial/modules.html#intra-package-references
“In fact, such references are so common that the import statement first looks in the containing package before looking in the standard module search path.”

(!!!) Здесь стоит заметить отсутствие упоминания данного факта в другом месте того же документа (http://docs.python.org/2/tutorial/modules.html#the-module-search-path), что вводит в заблуждение (см. bugs.python.org/issue16891).

(!!!) Второе, что следует отметить — это что данный шаг поиска присутствует только в случае, если модуль, в котором исполняется import abcd сам импортирован из пакета (т.е. с помощью инструкции import <имя пакета>.<имя модуля>). В случаях импорта этого модуля без указания пакета, либо выполнения модуля как скрипта данный шаг будет пропущен. Это отражено в документе www.python.org/dev/peps/pep-0302/#id23:
“The built-in __import__ function (known as PyImport_ImportModuleEx() in import.c) will then check to see whether the module doing the import is a package or a submodule of a package. If it is indeed a (submodule of a) package, it first tries to do the import relative to the package (the parent package for a submodule). For example if a package named «spam» does «import eggs», it will first look for a module named «spam.eggs». If that fails, the import continues as an absolute import: it will look for a module named «eggs».”

*** docs.python.org/2/tutorial/modules.html#the-module-search-path
“When a module named spam is imported, the interpreter first searches for a built-in module with that name.”

**** docs.python.org/2/tutorial/modules.html#the-module-search-path
“If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path.”

Алгоритм поиска имен для import abcd.defg



Сначала производится поиск пакета или модуля abcd в соответствии с алгоритмом, описанным для import abcd.

Если поиск успешный, то выполняется поиск пакета или модуля defg в соответствии со следующим алгоритмом:
Что ищем Где ищем* Комментарий
1 пакет defg в пакете abcd ссылка на документацию указана в **
2 модуль defg в пакете abcd ссылка на документацию указана в **

* В случае, если в результате поиска abcd, последний оказался модулем, то импорт закончится ошибкой ImportError: No module named defg, т.к. модуль не может содержать другие модули, либо пакеты:
docs.python.org/2/reference/simple_stmts.html#import: “A package can contain other packages and modules while modules cannot contain other modules or packages.”
** www.python.org/dev/peps/pep-0302/#id23
“Deeper down in the mechanism, a dotted name import is split up by its components. For «import spam.ham», first an «import spam» is done, and only when that succeeds is «ham» imported as a submodule of «spam».”

Алгоритм поиска имен для from abcd.defg import ghi



Сначала производится поиск пакета или модуля abcd.defg в соответствии с алгоритмом, описанным для import abcd.defg:
Что ищем Где ищем
1 имя ghi в пакете или модуле defg
2 пакет ghi в пакете defg
3 модуль ghi в пакете defg

Перекрытие имен



Хочу отметить одну интересную особенность, которая следует из последовательного применения перечисленных выше алгоритмов. Представьте следующую ситуацию: существует модуль с именем abcd и пакет с именем abcd, содержащий, в свою очередь, модуль defg, модуль abcd и пакет abcd размещены в разных каталогах, при этом модуль abcd размещен в том же пакете, что и модуль, в котором выполняется инструкция import abcd.defg. В этом случае выполнение импорта завершится с ошибкой. Это связано с тем, что интерпретатор Python сначала найдет модуль abcd, потом попытается искать в нем модуль defg, что невозможно.

Разумнее было бы определить из синтаксиса инструкции import, что abcd может быть только пакетом (т.к. все элементы до точки могут являться только пакетами) и искать abcd только как пакет. В этом случае пакет abcd был бы импортирован из другого каталога, а в нем был бы обнаружен модуль defg и выполнение программы продолжилось бы без ошибок.

К сожалению, такое поведение Python не реализовано. См. bugs.python.org/issue16891#msg179353.

Автор статьи столкнулся с данной проблемой, но в связи разразненностью описания в официальной документации Python потребовалось некорое время для выяснения причин такого поведения интерпретатора. В результате возникли следующие обсуждения stackoverflow.com/questions/14183541/why-python-finds-module-instead-of-package-if-they-have-the-same-name и bugs.python.org/issue16891, а также написана данная статья.

В случае возникновения подобных коллизий имен возможны следующие решения:
  1. Переименовать модуль, либо пакет таким образом, чтобы имя пакета не совпадало с именем модуля, т.е. исключить совпадение имен
  2. Иногда может помочь включение абсолютных путей по умолчанию для импорта с помощью инструкции from __future__ import absolute_import (на самом деле в этом случае, лишь повышается контроль за последовательностью поиска пакетов и модулей путем внесения изменений в sys.path)


Приложение: исходный код


В этом репозитории находится исходный код, демонстрирующий описанные выше алгоритмы: bitbucket.org/dmugtasimov/python_import.
Tags:
Hubs:
Total votes 34: ↑32 and ↓2+30
Comments14

Articles