- PVSM.RU - https://www.pvsm.ru -
Начать, видимо, следует с того, что речь пойдет об интерпретаторе CPython версии 2.7.x (примеры проверялись на версии 2.7.3).
На официальном сайте имеются описания инструкции import и модулей в Python:
Из них следует, что в Python имеются пакеты (package), модули (module) и имена, определенные в модулях (names). Также следует отметить, что в некоторых частях документации модули называются подмоудлями (submodule), если они размещены внутри пакета.
В языке Python инструкция import позволяет импортировать пакеты, модули и имена в пространство имен, в котором инструкция import выполняется. При это существует две интересные особенности:
Из этих двух особенностей следуют такие неоднозначности для записи import abcd:
Еще примеры неоднозначностей:
Для разрешения эти декларативных неоднозначностей должен существовать императивный алгоритм. Такой алгоритм в некотором виде описан в официальной документации Python.
Поиск имени 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 [4]
“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 [5]).
(!!!) Второе, что следует отметить — это что данный шаг поиска присутствует только в случае, если модуль, в котором исполняется import abcd сам импортирован из пакета (т.е. с помощью инструкции import <имя пакета>.<имя модуля>). В случаях импорта этого модуля без указания пакета, либо выполнения модуля как скрипта данный шаг будет пропущен. Это отражено в документе www.python.org/dev/peps/pep-0302/#id23 [6]:
“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 [7]
“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 [7]
“If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path.”
Сначала производится поиск пакета или модуля 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 [1]: “A package can contain other packages and modules while modules cannot contain other modules or packages.”
** www.python.org/dev/peps/pep-0302/#id23 [6]
“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».”
Сначала производится поиск пакета или модуля 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 [8].
Автор статьи столкнулся с данной проблемой, но в связи разразненностью описания в официальной документации Python потребовалось некорое время для выяснения причин такого поведения интерпретатора. В результате возникли следующие обсуждения stackoverflow.com/questions/14183541/why-python-finds-module-instead-of-package-if-they-have-the-same-name [9] и bugs.python.org/issue16891 [5], а также написана данная статья.
В случае возникновения подобных коллизий имен возможны следующие решения:
В этом репозитории находится исходный код, демонстрирующий описанные выше алгоритмы: bitbucket.org/dmugtasimov/python_import [10].
Автор: dmugtasimov
Источник [11]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/25277
Ссылки в тексте:
[1] docs.python.org/2/reference/simple_stmts.html#import: http://docs.python.org/2/reference/simple_stmts.html#import
[2] docs.python.org/2/tutorial/modules.html: http://docs.python.org/2/tutorial/modules.html
[3] www.python.org/dev/peps/pep-0328/: http://www.python.org/dev/peps/pep-0328/
[4] docs.python.org/2/tutorial/modules.html#intra-package-references: http://docs.python.org/2/tutorial/modules.html#intra-package-references
[5] bugs.python.org/issue16891: http://bugs.python.org/issue16891
[6] www.python.org/dev/peps/pep-0302/#id23: http://www.python.org/dev/peps/pep-0302/#id23
[7] docs.python.org/2/tutorial/modules.html#the-module-search-path: http://docs.python.org/2/tutorial/modules.html#the-module-search-path
[8] bugs.python.org/issue16891#msg179353: http://bugs.python.org/issue16891#msg179353
[9] stackoverflow.com/questions/14183541/why-python-finds-module-instead-of-package-if-they-have-the-same-name: http://stackoverflow.com/questions/14183541/why-python-finds-module-instead-of-package-if-they-have-the-same-name
[10] bitbucket.org/dmugtasimov/python_import: https://bitbucket.org/dmugtasimov/python_import
[11] Источник: http://habrahabr.ru/post/166463/
Нажмите здесь для печати.